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 testHaugValues() { // Testing cash-or-nothing double barrier options against Haug's values DoubleBinaryOptionData[] values = { /* The data below are from * "Option pricing formulas 2nd Ed.", E.G. Haug, McGraw-Hill 2007 pag. 181 * Note: book uses cost of carry b, instead of dividend rate q */ // barrierType, bar_lo, bar_hi, cash, spot, q, r, t, vol, value, tol new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 9.8716, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 8.9307, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 6.3272, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 1.9094, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 9.7961, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 7.2300, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 3.7100, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 0.4271, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 8.9054, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 3.6752, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 0.7960, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 0.0059, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 3.6323, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 0.0911, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 0.0002, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 0.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 0.2402, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 1.4076, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 3.8160, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.0075, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 0.9910, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 2.8098, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 4.6612, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.2656, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 2.7954, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 4.4024, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 4.9266, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 2.6285, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 4.7523, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 4.9096, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 4.9675, 1e-4), // following values calculated with haug's VBA code new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.0042, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 0.9450, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 3.5486, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 7.9663, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.0797, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 2.6458, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 6.1658, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 9.4486, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.9704, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 6.2006, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 9.0798, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 9.8699, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 6.2434, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 9.7847, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 9.8756, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 9.8758, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.0041, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 0.7080, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 2.1581, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 80.00, 120.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 4.2061, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.0723, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 1.6663, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 3.3930, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 85.00, 115.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 4.8679, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 0.7080, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 3.4424, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 4.7496, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 90.00, 110.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 5.0475, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.10, 3.6524, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.20, 5.1256, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.30, 5.0763, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 95.00, 105.00, 10.00, 100.00, 0.02, 0.05, 0.25, 0.50, 5.0275, 1e-4), // degenerate cases new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 95.00, 105.00, 10.00, 80.00, 0.02, 0.05, 0.25, 0.10, 0.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockOut, 95.00, 105.00, 10.00, 110.00, 0.02, 0.05, 0.25, 0.10, 0.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 95.00, 105.00, 10.00, 80.00, 0.02, 0.05, 0.25, 0.10, 10.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KnockIn, 95.00, 105.00, 10.00, 110.00, 0.02, 0.05, 0.25, 0.10, 10.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 95.00, 105.00, 10.00, 80.00, 0.02, 0.05, 0.25, 0.10, 10.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KIKO, 95.00, 105.00, 10.00, 110.00, 0.02, 0.05, 0.25, 0.10, 0.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 95.00, 105.00, 10.00, 80.00, 0.02, 0.05, 0.25, 0.10, 0.0000, 1e-4), new DoubleBinaryOptionData(DoubleBarrier.Type.KOKI, 95.00, 105.00, 10.00, 110.00, 0.02, 0.05, 0.25, 0.10, 10.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(Option.Type.Call, 0, values[i].cash); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise; if (values[i].barrierType == DoubleBarrier.Type.KIKO || values[i].barrierType == DoubleBarrier.Type.KOKI) { exercise = new AmericanExercise(today, exDate, true); } else { 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 AnalyticDoubleBarrierBinaryEngine(stochProcess); DoubleBarrierOption opt = new DoubleBarrierOption(values[i].barrierType, values[i].barrier_lo, values[i].barrier_hi, 0, payoff, exercise); opt.setPricingEngine(engine); double calculated = opt.NPV(); double expected = values[i].result; double error = Math.Abs(calculated - values[i].result); if (error > values[i].tol) { REPORT_FAILURE("value", payoff, exercise, values[i].barrierType, values[i].barrier_lo, values[i].barrier_hi, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, values[i].tol); } int steps = 500; // checking with binomial engine engine = new BinomialDoubleBarrierEngine( (d, end, step, strike) => new CoxRossRubinstein(d, end, step, strike), (args, process, grid) => new DiscretizedDoubleBarrierOption(args, process, grid), stochProcess, steps); opt.setPricingEngine(engine); calculated = opt.NPV(); expected = values[i].result; error = Math.Abs(calculated - expected); double tol = 0.22; if (error > tol) { REPORT_FAILURE("Binomial value", payoff, exercise, values[i].barrierType, values[i].barrier_lo, values[i].barrier_hi, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, tol); } } }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(AmericanExercise obj) { return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; }
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); } } }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(AmericanExercise obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }
public void testBond() { /* when deeply out-of-the-money, the value of the convertible bond * should equal that of the underlying plain-vanilla bond. */ // Testing out-of-the-money convertible bonds against vanilla bonds CommonVars vars = new CommonVars(); vars.conversionRatio = 1.0e-16; Exercise euExercise = new EuropeanExercise(vars.maturityDate); Exercise amExercise = new AmericanExercise(vars.issueDate, vars.maturityDate); int timeSteps = 1001; IPricingEngine engine = new BinomialConvertibleEngine <CoxRossRubinstein>(vars.process, timeSteps); Handle <YieldTermStructure> discountCurve = new Handle <YieldTermStructure>(new ForwardSpreadedTermStructure(vars.riskFreeRate, vars.creditSpread)); // zero-coupon Schedule schedule = new MakeSchedule().from(vars.issueDate) .to(vars.maturityDate) .withFrequency(Frequency.Once) .withCalendar(vars.calendar) .backwards().value(); ConvertibleZeroCouponBond euZero = new ConvertibleZeroCouponBond(euExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, vars.dayCounter, schedule, vars.redemption); euZero.setPricingEngine(engine); ConvertibleZeroCouponBond amZero = new ConvertibleZeroCouponBond(amExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, vars.dayCounter, schedule, vars.redemption); amZero.setPricingEngine(engine); ZeroCouponBond zero = new ZeroCouponBond(vars.settlementDays, vars.calendar, 100.0, vars.maturityDate, BusinessDayConvention.Following, vars.redemption, vars.issueDate); IPricingEngine bondEngine = new DiscountingBondEngine(discountCurve); zero.setPricingEngine(bondEngine); double tolerance = 1.0e-2 * (vars.faceAmount / 100.0); double error = Math.Abs(euZero.NPV() - zero.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce zero-coupon bond price:" + "\n calculated: " + euZero.NPV() + "\n expected: " + zero.settlementValue() + "\n error: " + error); } error = Math.Abs(amZero.NPV() - zero.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce zero-coupon bond price:" + "\n calculated: " + amZero.NPV() + "\n expected: " + zero.settlementValue() + "\n error: " + error); } // coupon List <double> coupons = new InitializedList <double>(1, 0.05); schedule = new MakeSchedule().from(vars.issueDate) .to(vars.maturityDate) .withFrequency(vars.frequency) .withCalendar(vars.calendar) .backwards().value(); ConvertibleFixedCouponBond euFixed = new ConvertibleFixedCouponBond(euExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, coupons, vars.dayCounter, schedule, vars.redemption); euFixed.setPricingEngine(engine); ConvertibleFixedCouponBond amFixed = new ConvertibleFixedCouponBond(amExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, coupons, vars.dayCounter, schedule, vars.redemption); amFixed.setPricingEngine(engine); FixedRateBond fixedBond = new FixedRateBond(vars.settlementDays, vars.faceAmount, schedule, coupons, vars.dayCounter, BusinessDayConvention.Following, vars.redemption, vars.issueDate); fixedBond.setPricingEngine(bondEngine); tolerance = 2.0e-2 * (vars.faceAmount / 100.0); error = Math.Abs(euFixed.NPV() - fixedBond.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce fixed-coupon bond price:" + "\n calculated: " + euFixed.NPV() + "\n expected: " + fixedBond.settlementValue() + "\n error: " + error); } error = Math.Abs(amFixed.NPV() - fixedBond.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce fixed-coupon bond price:" + "\n calculated: " + amFixed.NPV() + "\n expected: " + fixedBond.settlementValue() + "\n error: " + error); } // floating-rate IborIndex index = new Euribor1Y(discountCurve); int fixingDays = 2; List <double> gearings = new InitializedList <double>(1, 1.0); List <double> spreads = new List <double>(); ConvertibleFloatingRateBond euFloating = new ConvertibleFloatingRateBond(euExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, index, fixingDays, spreads, vars.dayCounter, schedule, vars.redemption); euFloating.setPricingEngine(engine); ConvertibleFloatingRateBond amFloating = new ConvertibleFloatingRateBond(amExercise, vars.conversionRatio, vars.no_dividends, vars.no_callability, vars.creditSpread, vars.issueDate, vars.settlementDays, index, fixingDays, spreads, vars.dayCounter, schedule, vars.redemption); amFloating.setPricingEngine(engine); IborCouponPricer pricer = new BlackIborCouponPricer(new Handle <OptionletVolatilityStructure>()); Schedule floatSchedule = new Schedule(vars.issueDate, vars.maturityDate, new Period(vars.frequency), vars.calendar, BusinessDayConvention.Following, BusinessDayConvention.Following, DateGeneration.Rule.Backward, false); FloatingRateBond floating = new FloatingRateBond(vars.settlementDays, vars.faceAmount, floatSchedule, index, vars.dayCounter, BusinessDayConvention.Following, fixingDays, gearings, spreads, new List <double?>(), new List <double?>(), false, vars.redemption, vars.issueDate); floating.setPricingEngine(bondEngine); Utils.setCouponPricer(floating.cashflows(), pricer); tolerance = 2.0e-2 * (vars.faceAmount / 100.0); error = Math.Abs(euFloating.NPV() - floating.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce floating-rate bond price:" + "\n calculated: " + euFloating.NPV() + "\n expected: " + floating.settlementValue() + "\n error: " + error); } error = Math.Abs(amFloating.NPV() - floating.settlementValue()); if (error > tolerance) { QAssert.Fail("failed to reproduce floating-rate bond price:" + "\n calculated: " + amFloating.NPV() + "\n expected: " + floating.settlementValue() + "\n error: " + error); } }
public void testFdGreeks <Engine>() where Engine : IFDEngine, new() { //SavedSettings backup; Dictionary <string, double> calculated = new Dictionary <string, double>(), expected = new Dictionary <string, double>(), tolerance = new Dictionary <string, double>(); tolerance.Add("delta", 7.0e-4); tolerance.Add("gamma", 2.0e-4); //tolerance["theta"] = 1.0e-4; Option.Type[] types = new Option.Type[] { Option.Type.Call, Option.Type.Put }; double[] strikes = { 50.0, 99.5, 100.0, 100.5, 150.0 }; double[] underlyings = { 100.0 }; double[] qRates = { 0.04, 0.05, 0.06 }; double[] rRates = { 0.01, 0.05, 0.15 }; int[] years = { 1, 2 }; double[] vols = { 0.11, 0.50, 1.20 }; Date today = Date.Today; Settings.setEvaluationDate(today); DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); for (int i = 0; i < types.Length; i++) { for (int j = 0; j < strikes.Length; j++) { for (int k = 0; k < years.Length; k++) { Date exDate = today + new Period(years[k], TimeUnit.Years); Exercise exercise = new AmericanExercise(today, exDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strikes[j]); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new Engine().factory(stochProcess); VanillaOption option = new VanillaOption(payoff, exercise); option.setPricingEngine(engine); for (int l = 0; l < underlyings.Length; l++) { for (int m = 0; m < qRates.Length; m++) { for (int n = 0; n < rRates.Length; n++) { for (int p = 0; p < vols.Length; p++) { double u = underlyings[l]; double q = qRates[m], r = rRates[n]; double v = vols[p]; spot.setValue(u); qRate.setValue(q); rRate.setValue(r); vol.setValue(v); double value = option.NPV(); calculated.Add("delta", option.delta()); calculated.Add("gamma", option.gamma()); //calculated["theta"] = option.theta(); if (value > spot.value() * 1.0e-5) { // perturb spot and get delta and gamma double du = u * 1.0e-4; spot.setValue(u + du); double value_p = option.NPV(), delta_p = option.delta(); spot.setValue(u - du); double value_m = option.NPV(), delta_m = option.delta(); spot.setValue(u); expected.Add("delta", (value_p - value_m) / (2 * du)); expected.Add("gamma", (delta_p - delta_m) / (2 * du)); /* * // perturb date and get theta * Time dT = dc.yearFraction(today-1, today+1); * Settings::instance().setEvaluationDate(today-1); * value_m = option.NPV(); * Settings::instance().setEvaluationDate(today+1); * value_p = option.NPV(); * Settings::instance().setEvaluationDate(today); * expected["theta"] = (value_p - value_m)/dT; */ // compare foreach (string greek in calculated.Keys) { double expct = expected [greek], calcl = calculated[greek], tol = tolerance [greek]; double error = Utilities.relativeError(expct, calcl, u); if (error > tol) { REPORT_FAILURE(greek, payoff, exercise, u, q, r, today, v, expct, calcl, error, tol); } } } calculated.Clear(); expected.Clear(); } } } } } } } }
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); } } }
static void Main(string[] args) { try { Option.Type type = Option.Type.Put; double underlying = 36.0; double spreadRate = 0.005; double dividendYield = 0.02; double riskFreeRate = 0.06; double volatility = 0.2; int settlementDays = 3; int length = 5; double redemption = 100.0; double conversionRatio = redemption / underlying; // at the money // set up dates/schedules Calendar calendar = new TARGET(); Date today = calendar.adjust(Date.Today); Settings.setEvaluationDate(today); Date settlementDate = calendar.advance(today, settlementDays, TimeUnit.Days); Date exerciseDate = calendar.advance(settlementDate, length, TimeUnit.Years); Date issueDate = calendar.advance(exerciseDate, -length, TimeUnit.Years); BusinessDayConvention convention = BusinessDayConvention.ModifiedFollowing; Frequency frequency = Frequency.Annual; Schedule schedule = new Schedule(issueDate, exerciseDate, new Period(frequency), calendar, convention, convention, DateGeneration.Rule.Backward, false); DividendSchedule dividends = new DividendSchedule(); CallabilitySchedule callability = new CallabilitySchedule(); List <double> coupons = new InitializedList <double>(1, 0.05); DayCounter bondDayCount = new Thirty360(); int[] callLength = { 2, 4 }; // Call dates, years 2,4. int[] putLength = { 3 }; // Put dates year 3. double[] callPrices = { 101.5, 100.85 }; double[] putPrices = { 105.0 }; // Load call schedules for (int i = 0; i < callLength.Length; i++) { SoftCallability s = new SoftCallability( new Callability.Price(callPrices[i], Callability.Price.Type.Clean), schedule.date(callLength[i]), 1.20); callability.Add(s); } for (int j = 0; j < putLength.Length; j++) { Callability s = new Callability(new Callability.Price(putPrices[j], Callability.Price.Type.Clean), Callability.Type.Put, schedule.date(putLength[j])); callability.Add(s); } // Assume dividends are paid every 6 months . for (Date d = today + new Period(6, TimeUnit.Months); d < exerciseDate; d += new Period(6, TimeUnit.Months)) { Dividend div = new FixedDividend(1.0, d); dividends.Add(div); } DayCounter dayCounter = new Actual365Fixed(); double maturity = dayCounter.yearFraction(settlementDate, exerciseDate); Console.WriteLine("option type = " + type); Console.WriteLine("Time to maturity = " + maturity); Console.WriteLine("Underlying price = " + underlying); Console.WriteLine("Risk-free interest rate = {0:0.0%}", riskFreeRate); Console.WriteLine("Dividend yield = {0:0.0%}%", dividendYield); Console.WriteLine("Volatility = {0:0.0%}%", volatility); Console.WriteLine(""); // write column headings int[] widths = { 35, 14, 14 }; int totalWidth = widths[0] + widths[1] + widths[2]; string rule = new string('-', totalWidth); string dblrule = new string('=', totalWidth); Console.WriteLine(dblrule); Console.WriteLine("Tsiveriotis-Fernandes method"); Console.WriteLine(dblrule); Console.WriteLine("Tree Type European American "); Console.WriteLine(rule); Exercise exercise = new EuropeanExercise(exerciseDate); Exercise amexercise = new AmericanExercise(settlementDate, exerciseDate); Handle <Quote> underlyingH = new Handle <Quote>(new SimpleQuote(underlying)); Handle <YieldTermStructure> flatTermStructure = new Handle <YieldTermStructure>(new FlatForward(settlementDate, riskFreeRate, dayCounter)); Handle <YieldTermStructure> flatDividendTS = new Handle <YieldTermStructure>(new FlatForward(settlementDate, dividendYield, dayCounter)); Handle <BlackVolTermStructure> flatVolTS = new Handle <BlackVolTermStructure>(new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)); BlackScholesMertonProcess stochasticProcess = new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS); int timeSteps = 801; Handle <Quote> creditSpread = new Handle <Quote>(new SimpleQuote(spreadRate)); Quote rate = new SimpleQuote(riskFreeRate); Handle <YieldTermStructure> discountCurve = new Handle <YieldTermStructure>(new FlatForward(today, new Handle <Quote>(rate), dayCounter)); IPricingEngine engine = new BinomialConvertibleEngine <JarrowRudd>(stochasticProcess, timeSteps); ConvertibleFixedCouponBond europeanBond = new ConvertibleFixedCouponBond(exercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); europeanBond.setPricingEngine(engine); ConvertibleFixedCouponBond americanBond = new ConvertibleFixedCouponBond(amexercise, conversionRatio, dividends, callability, creditSpread, issueDate, settlementDays, coupons, bondDayCount, schedule, redemption); americanBond.setPricingEngine(engine); Console.WriteLine("Jarrow-Rudd {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <CoxRossRubinstein>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <CoxRossRubinstein>(stochasticProcess, timeSteps)); Console.WriteLine("CoxRossRubinstein {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <AdditiveEQPBinomialTree>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <AdditiveEQPBinomialTree>(stochasticProcess, timeSteps)); Console.WriteLine("AdditiveEQPBinomialTree {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <Trigeorgis>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <Trigeorgis>(stochasticProcess, timeSteps)); Console.WriteLine("Trigeorgis {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <Tian>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <Tian>(stochasticProcess, timeSteps)); Console.WriteLine("Tian {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <LeisenReimer>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <LeisenReimer>(stochasticProcess, timeSteps)); Console.WriteLine("LeisenReimer {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); americanBond.setPricingEngine(new BinomialConvertibleEngine <Joshi4>(stochasticProcess, timeSteps)); europeanBond.setPricingEngine(new BinomialConvertibleEngine <Joshi4>(stochasticProcess, timeSteps)); Console.WriteLine("Joshi4 {0:0.000000} {1:0.000000}", europeanBond.NPV(), americanBond.NPV()); Console.WriteLine("==========================================================================="); } catch (Exception e) { Console.WriteLine(e.ToString()); } Console.ReadKey(); }
public void testBaroneAdesiWhaleyValues() { // ("Testing Barone-Adesi and Whaley approximation for American options..."); /* The data below are from * "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 pag 24 * * The following values were replicated only up to the second digit * by the VB code provided by Haug, which was used as base for the * C++ implementation * */ AmericanOptionData[] values = { new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 0.0206), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8771), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 10.0089), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 0.3159), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1280), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 10.3919), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 0.9495), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3777), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 11.1679), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 0.8208), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0842), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 10.8087), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 2.7437), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.8015), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 13.0170), new AmericanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 5.0063), new AmericanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.5106), new AmericanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 15.5689), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 10.0000), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8770), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 0.0410), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 10.2533), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1277), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 0.4562), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 10.8787), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3777), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 1.2402), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 10.5595), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0842), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 1.0822), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 12.4419), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.8014), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 3.3226), new AmericanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 14.6945), new AmericanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.5104), new AmericanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 5.8823) }; Date today = Date.Today; DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); double tolerance = 3.0e-3; for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new AmericanExercise(today, exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new BaroneAdesiWhaleyApproximationEngine(stochProcess); VanillaOption option = new VanillaOption(payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, tolerance); } } }
public void testBjerksundStenslandValues() { // ("Testing Bjerksund and Stensland approximation for American options..."); AmericanOptionData[] values = new AmericanOptionData[] { // type, strike, spot, q, r, t, vol, value, tol // from "Option pricing formulas", Haug, McGraw-Hill 1998, pag 27 new AmericanOptionData(Option.Type.Call, 40.00, 42.00, 0.08, 0.04, 0.75, 0.35, 5.2704), // from "Option pricing formulas", Haug, McGraw-Hill 1998, VBA code new AmericanOptionData(Option.Type.Put, 40.00, 36.00, 0.00, 0.06, 1.00, 0.20, 4.4531), // ATM option with very small volatility, reference value taken from R new AmericanOptionData(Option.Type.Call, 100, 100, 0.05, 0.05, 1.0, 0.0021, 0.08032314), // ATM option with very small volatility, // reference value taken from Barone-Adesi and Whaley Approximation new AmericanOptionData(Option.Type.Call, 100, 100, 0.05, 0.05, 1.0, 0.0001, 0.003860656), new AmericanOptionData(Option.Type.Call, 100, 99.99, 0.05, 0.05, 1.0, 0.0001, 0.00081), // ITM option with a very small volatility new AmericanOptionData(Option.Type.Call, 100, 110, 0.05, 0.05, 1.0, 0.0001, 10.0), new AmericanOptionData(Option.Type.Put, 110, 100, 0.05, 0.05, 1.0, 0.0001, 10.0), // ATM option with a very large volatility new AmericanOptionData(Option.Type.Put, 100, 110, 0.05, 0.05, 1.0, 10, 94.89543) }; Date today = Date.Today; DayCounter dc = new Actual360(); SimpleQuote spot = new SimpleQuote(0.0); SimpleQuote qRate = new SimpleQuote(0.0); YieldTermStructure qTS = Utilities.flatRate(today, qRate, dc); SimpleQuote rRate = new SimpleQuote(0.0); YieldTermStructure rTS = Utilities.flatRate(today, rRate, dc); SimpleQuote vol = new SimpleQuote(0.0); BlackVolTermStructure volTS = Utilities.flatVol(today, vol, dc); double tolerance = 5.0e-5; for (int i = 0; i < values.Length; i++) { StrikedTypePayoff payoff = new PlainVanillaPayoff(values[i].type, values[i].strike); Date exDate = today + Convert.ToInt32(values[i].t * 360 + 0.5); Exercise exercise = new AmericanExercise(today, exDate); spot.setValue(values[i].s); qRate.setValue(values[i].q); rRate.setValue(values[i].r); vol.setValue(values[i].v); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(new Handle <Quote>(spot), new Handle <YieldTermStructure>(qTS), new Handle <YieldTermStructure>(rTS), new Handle <BlackVolTermStructure>(volTS)); IPricingEngine engine = new BjerksundStenslandApproximationEngine(stochProcess); VanillaOption option = new VanillaOption(payoff, exercise); option.setPricingEngine(engine); double calculated = option.NPV(); double error = Math.Abs(calculated - values[i].result); if (error > tolerance) { REPORT_FAILURE("value", payoff, exercise, values[i].s, values[i].q, values[i].r, today, values[i].v, values[i].result, calculated, error, tolerance); } } }
public static string eqInstAmericanOptionBaroneAdesiWhaley( [ExcelArgument(Description = "id of option to be constructed ")] string ObjectId, [ExcelArgument(Description = "Option type ")] string optype, [ExcelArgument(Description = "Spot price ")] double underlyingprice, [ExcelArgument(Description = "Strike price ")] double stirkeprice, [ExcelArgument(Description = "Expiry Date ")] DateTime date, [ExcelArgument(Description = "Risk free rate ")] double riskfreerate, [ExcelArgument(Description = "dividend/convenience rate ")] double dividendrate, [ExcelArgument(Description = "Black-Scholes Vol ")] double volatility, [ExcelArgument(Description = "DayCounter ")] string daycounter, [ExcelArgument(Description = "Calendar ")] string calendar, [ExcelArgument(Description = "trigger ")] object trigger) { if (ExcelUtil.CallFromWizard()) { return(""); } string callerAddress = ""; callerAddress = ExcelUtil.getActiveCellAddress(); try { if (date == DateTime.MinValue) { throw new Exception("Date must not be empty. "); } if (ExcelUtil.isNull(daycounter)) { daycounter = "ACTUAL365"; } if (ExcelUtil.isNull(calendar)) { calendar = "NYC"; } Option.Type optiontype; if (optype.ToUpper() == "CALL") { optiontype = Option.Type.Call; } else if (optype.ToUpper() == "PUT") { optiontype = Option.Type.Put; } else { throw new Exception("Unknow option type"); } EliteQuant.Calendar cal = EliteQuant.EQConverter.ConvertObject <EliteQuant.Calendar>(calendar); EliteQuant.DayCounter dc = EliteQuant.EQConverter.ConvertObject <EliteQuant.DayCounter>(daycounter); EliteQuant.Date maturitydate = EliteQuant.EQConverter.ConvertObject <EliteQuant.Date>(date); EliteQuant.Date today = EliteQuant.Settings.instance().getEvaluationDate(); EliteQuant.Date settlementdate = today; // T+2 if (maturitydate.serialNumber() <= today.serialNumber()) { throw new Exception("Option already expired."); } AmericanExercise americanExercise = new AmericanExercise(today, maturitydate); QuoteHandle underlyingQuoteH = new QuoteHandle(new EliteQuant.SimpleQuote(underlyingprice)); YieldTermStructureHandle flatRateTSH = new YieldTermStructureHandle( new FlatForward(settlementdate, riskfreerate, dc)); YieldTermStructureHandle flatDividendTSH = new YieldTermStructureHandle( new FlatForward(settlementdate, dividendrate, dc)); BlackVolTermStructureHandle flatVolTSH = new BlackVolTermStructureHandle( new BlackConstantVol(settlementdate, cal, volatility, dc)); BlackScholesMertonProcess stochasticProcess = new BlackScholesMertonProcess(underlyingQuoteH, flatDividendTSH, flatRateTSH, flatVolTSH); PlainVanillaPayoff payoff = new PlainVanillaPayoff(optiontype, stirkeprice); VanillaOption europeanOption = new VanillaOption(payoff, americanExercise); europeanOption.setPricingEngine(new BaroneAdesiWhaleyEngine(stochasticProcess)); // Store the option and return its id string id = "OPTION@" + ObjectId; OHRepository.Instance.storeObject(id, europeanOption, callerAddress); id += "#" + (String)DateTime.Now.ToString(@"HH:mm:ss"); return(id); } catch (Exception e) { ExcelUtil.logError(callerAddress, System.Reflection.MethodInfo.GetCurrentMethod().Name.ToString(), e.Message); return("#EQ_ERR!"); } }
public static string eqInstVanillaOption( [ExcelArgument(Description = "id of option to be constructed ")] string ObjectId, [ExcelArgument(Description = "Option type E(uropean), A(merican), B(ermudan) ")] string exercisetype, [ExcelArgument(Description = "CALL or PUT ")] string optype, [ExcelArgument(Description = "Strike price ")] double strikeprice, [ExcelArgument(Description = "Expiry Dates (E = 1, A = 2, B = many) ")] object[] dates) { if (ExcelUtil.CallFromWizard()) { return(""); } string callerAddress = ""; callerAddress = ExcelUtil.getActiveCellAddress(); try { Exercise exercise = null; if (exercisetype.ToUpper() == "E") { Date maturitydate = EliteQuant.EQConverter.DateTimeToDate(DateTime.FromOADate((double)dates[0])); // assume first date exercise = new EuropeanExercise(maturitydate); } else if (exercisetype.ToUpper() == "A") { Date earliestdate = EliteQuant.EQConverter.DateTimeToDate(DateTime.FromOADate((double)dates[0])); // assume first date Date lastdate = EliteQuant.EQConverter.DateTimeToDate(DateTime.FromOADate((double)dates[1])); // assume last date exercise = new AmericanExercise(earliestdate, lastdate); } else if (exercisetype.ToUpper() == "B") { DateVector dv = new DateVector(); foreach (var dt in dates) { Date dte = EliteQuant.EQConverter.DateTimeToDate(DateTime.FromOADate((double)dt)); dv.Add(dte); } exercise = new BermudanExercise(dv); } else { throw new Exception("Unknow exercise type "); } Option.Type optiontype; if (optype.ToUpper() == "CALL") { optiontype = Option.Type.Call; } else if (optype.ToUpper() == "PUT") { optiontype = Option.Type.Put; } else { throw new Exception("Unknow option type"); } PlainVanillaPayoff payoff = new PlainVanillaPayoff(optiontype, strikeprice); VanillaOption europeanOption = new VanillaOption(payoff, exercise); // Store the option and return its id string id = "OPTION@" + ObjectId; OHRepository.Instance.storeObject(id, europeanOption, callerAddress); id += "#" + (String)DateTime.Now.ToString(@"HH:mm:ss"); return(id); } catch (Exception e) { ExcelUtil.logError(callerAddress, System.Reflection.MethodInfo.GetCurrentMethod().Name.ToString(), e.Message); return("#EQ_ERR!"); } }
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); } } }
private static void Main(string[] args) { DateTime startTime = DateTime.Now; var optionType = Option.Type.Put; double underlyingPrice = 36; double strikePrice = 40; double dividendYield = 0.0; double riskFreeRate = 0.06; double volatility = 0.2; var todaysDate = new Date(15, Month.May, 1998); Settings.instance().setEvaluationDate(todaysDate); var settlementDate = new Date(17, Month.May, 1998); var maturityDate = new Date(17, Month.May, 1999); Calendar calendar = new TARGET(); var exerciseDates = new DateVector(4); for (int i = 1; i <= 4; i++) { var forwardPeriod = new Period(3 * i, TimeUnit.Months); Date forwardDate = settlementDate.Add(forwardPeriod); exerciseDates.Add(forwardDate); } var europeanExercise = new EuropeanExercise(maturityDate); var bermudanExercise = new BermudanExercise(exerciseDates); var americanExercise = new AmericanExercise(settlementDate, maturityDate); // bootstrap the yield/dividend/vol curves and create a BlackScholesMerton stochastic process DayCounter dayCounter = new Actual365Fixed(); var flatRateTSH = new YieldTermStructureHandle(new FlatForward(settlementDate, riskFreeRate, dayCounter)); var flatDividendTSH = new YieldTermStructureHandle(new FlatForward(settlementDate, dividendYield, dayCounter)); var flatVolTSH = new BlackVolTermStructureHandle(new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)); var underlyingQuoteH = new QuoteHandle(new SimpleQuote(underlyingPrice)); var stochasticProcess = new BlackScholesMertonProcess(underlyingQuoteH, flatDividendTSH, flatRateTSH, flatVolTSH); var payoff = new PlainVanillaPayoff(optionType, strikePrice); // options var europeanOption = new VanillaOption(payoff, europeanExercise); var bermudanOption = new VanillaOption(payoff, bermudanExercise); var americanOption = new VanillaOption(payoff, americanExercise); // report the parameters we are using ReportParameters(optionType, underlyingPrice, strikePrice, dividendYield, riskFreeRate, volatility, maturityDate); // write out the column headings ReportHeadings(); #region Analytic Formulas // Black-Scholes for European try { europeanOption.setPricingEngine(new AnalyticEuropeanEngine(stochasticProcess)); ReportResults("Black-Scholes", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Barone-Adesi and Whaley approximation for American try { americanOption.setPricingEngine(new BaroneAdesiWhaleyEngine(stochasticProcess)); ReportResults("Barone-Adesi/Whaley", null, null, americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Bjerksund and Stensland approximation for American try { americanOption.setPricingEngine(new BjerksundStenslandEngine(stochasticProcess)); ReportResults("Bjerksund/Stensland", null, null, americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Integral try { europeanOption.setPricingEngine(new IntegralEngine(stochasticProcess)); ReportResults("Integral", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } uint timeSteps = 801; // Finite differences try { europeanOption.setPricingEngine(new FdBlackScholesVanillaEngine(stochasticProcess, timeSteps, timeSteps - 1)); bermudanOption.setPricingEngine(new FdBlackScholesVanillaEngine(stochasticProcess, timeSteps, timeSteps - 1)); americanOption.setPricingEngine(new FdBlackScholesVanillaEngine(stochasticProcess, timeSteps, timeSteps - 1)); ReportResults("Finite differences", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } //Variance Gamma try { var vgProcess = new VarianceGammaProcess(underlyingQuoteH, flatDividendTSH, flatRateTSH, volatility, 0.01, 0.0); europeanOption.setPricingEngine(new VarianceGammaEngine(vgProcess)); ReportResults("Variance-Gamma", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } #endregion Analytic Formulas #region Binomial Methods // Binomial Jarrow-Rudd try { europeanOption.setPricingEngine(new BinomialJRVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialJRVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialJRVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Jarrow-Rudd", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Cox-Ross-Rubinstein try { europeanOption.setPricingEngine(new BinomialCRRVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialCRRVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialCRRVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Cox-Ross-Rubinstein", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Additive Equiprobabilities try { europeanOption.setPricingEngine(new BinomialEQPVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialEQPVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialEQPVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Additive Equiprobabilities", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Trigeorgis try { europeanOption.setPricingEngine(new BinomialTrigeorgisVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialTrigeorgisVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialTrigeorgisVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Trigeorgis", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Tian try { europeanOption.setPricingEngine(new BinomialTianVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialTianVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialTianVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Tian", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Leisen-Reimer try { europeanOption.setPricingEngine(new BinomialLRVanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialLRVanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialLRVanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Leisen-Reimer", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } // Binomial Joshi try { europeanOption.setPricingEngine(new BinomialJ4VanillaEngine(stochasticProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialJ4VanillaEngine(stochasticProcess, timeSteps)); americanOption.setPricingEngine(new BinomialJ4VanillaEngine(stochasticProcess, timeSteps)); ReportResults("Binomial Joshi", europeanOption.NPV(), bermudanOption.NPV(), americanOption.NPV()); } catch (Exception e) { Console.WriteLine(e.ToString()); } #endregion Binomial Methods #region Monte Carlo Methods // quantlib appears to use max numeric (int and real) values to test for 'null' (or rather 'default') values // MC (crude) try { int mcTimeSteps = 1; int timeStepsPerYear = int.MaxValue; bool brownianBridge = false; bool antitheticVariate = false; int requiredSamples = int.MaxValue; double requiredTolerance = 0.02; int maxSamples = int.MaxValue; int seed = 42; europeanOption.setPricingEngine(new MCPREuropeanEngine(stochasticProcess, mcTimeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed)); ReportResults("MC (crude)", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } // MC (Sobol) try { int mcTimeSteps = 1; int timeStepsPerYear = int.MaxValue; bool brownianBridge = false; bool antitheticVariate = false; int requiredSamples = 32768; // 2^15 double requiredTolerance = double.MaxValue; int maxSamples = int.MaxValue; int seed = 0; europeanOption.setPricingEngine(new MCLDEuropeanEngine(stochasticProcess, mcTimeSteps, timeStepsPerYear, brownianBridge, antitheticVariate, requiredSamples, requiredTolerance, maxSamples, seed)); ReportResults("MC (Sobol)", europeanOption.NPV(), null, null); } catch (Exception e) { Console.WriteLine(e.ToString()); } #endregion Monte Carlo Methods DateTime endTime = DateTime.Now; TimeSpan delta = endTime - startTime; Console.WriteLine(); Console.WriteLine("Run completed in {0} s", delta.TotalSeconds); Console.WriteLine(); }
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); } } }
//static void Main(string[] args) //{ // List<double> xGrid = Enumerable.Range(0, 100).Select(x => x / 10.0).ToList(); // List<double> yGrid = Enumerable.Range(0, 100).Select(x => x / 10.0).ToList(); // //List<double> xGrid = Enumerable.Range(0, 100); // CubicInterpolation cubic = new CubicInterpolation(xGrid, xGrid.Count, yGrid, // CubicInterpolation.DerivativeApprox.Kruger, true, // CubicInterpolation.BoundaryCondition.SecondDerivative , 0.0, // CubicInterpolation.BoundaryCondition.SecondDerivative , 0.0); //} static void Main(string[] args) { DateTime timer = DateTime.Now; // set up dates Calendar calendar = new TARGET(); Date todaysDate = new Date(15, Month.May, 1998); Date settlementDate = new Date(17, Month.May, 1998); Settings.setEvaluationDate(todaysDate); // our options Option.Type type = Option.Type.Put; double underlying = 36; double strike = 40; double dividendYield = 0.00; double riskFreeRate = 0.06; double volatility = 0.20; Date maturity = new Date(17, Month.May, 1999); DayCounter dayCounter = new Actual365Fixed(); Console.WriteLine("Option type = " + type); Console.WriteLine("Maturity = " + maturity); Console.WriteLine("Underlying price = " + underlying); Console.WriteLine("Strike = " + strike); Console.WriteLine("Risk-free interest rate = {0:0.000000%}", riskFreeRate); Console.WriteLine("Dividend yield = {0:0.000000%}", dividendYield); Console.WriteLine("Volatility = {0:0.000000%}", volatility); Console.Write("\n"); string method; Console.Write("\n"); // write column headings int[] widths = new int[] { 35, 14, 14, 14 }; Console.Write("{0,-" + widths[0] + "}", "Method"); Console.Write("{0,-" + widths[1] + "}", "European"); Console.Write("{0,-" + widths[2] + "}", "Bermudan"); Console.WriteLine("{0,-" + widths[3] + "}", "American"); List <Date> exerciseDates = new List <Date>();; for (int i = 1; i <= 4; i++) { exerciseDates.Add(settlementDate + new Period(3 * i, TimeUnit.Months)); } Exercise europeanExercise = new EuropeanExercise(maturity); Exercise bermudanExercise = new BermudanExercise(exerciseDates); Exercise americanExercise = new AmericanExercise(settlementDate, maturity); Handle <Quote> underlyingH = new Handle <Quote>(new SimpleQuote(underlying)); // bootstrap the yield/dividend/vol curves var flatTermStructure = new Handle <YieldTermStructure>(new FlatForward(settlementDate, riskFreeRate, dayCounter)); var flatDividendTS = new Handle <YieldTermStructure>(new FlatForward(settlementDate, dividendYield, dayCounter)); var flatVolTS = new Handle <BlackVolTermStructure>(new BlackConstantVol(settlementDate, calendar, volatility, dayCounter)); StrikedTypePayoff payoff = new PlainVanillaPayoff(type, strike); var bsmProcess = new BlackScholesMertonProcess(underlyingH, flatDividendTS, flatTermStructure, flatVolTS); // options VanillaOption europeanOption = new VanillaOption(payoff, europeanExercise); VanillaOption bermudanOption = new VanillaOption(payoff, bermudanExercise); VanillaOption americanOption = new VanillaOption(payoff, americanExercise); // Analytic formulas: // Black-Scholes for European method = "Black-Scholes"; europeanOption.setPricingEngine(new AnalyticEuropeanEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + "}", "N/A"); europeanOption.theta(); // Barone-Adesi and Whaley approximation for American method = "Barone-Adesi/Whaley"; americanOption.setPricingEngine(new BaroneAdesiWhaleyApproximationEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + "}", "N/A"); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Bjerksund and Stensland approximation for American method = "Bjerksund/Stensland"; americanOption.setPricingEngine(new BjerksundStenslandApproximationEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + "}", "N/A"); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Integral method = "Integral"; europeanOption.setPricingEngine(new IntegralEngine(bsmProcess)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + "}", "N/A"); Console.WriteLine("{0,-" + widths[3] + "}", "N/A"); // Finite differences int timeSteps = 801; method = "Finite differences"; europeanOption.setPricingEngine(new FDEuropeanEngine(bsmProcess, timeSteps, timeSteps - 1)); bermudanOption.setPricingEngine(new FDBermudanEngine(bsmProcess, timeSteps, timeSteps - 1)); americanOption.setPricingEngine(new FDAmericanEngine(bsmProcess, timeSteps, timeSteps - 1)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Jarrow-Rudd method = "Binomial Jarrow-Rudd"; europeanOption.setPricingEngine(new BinomialVanillaEngine <JarrowRudd>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <JarrowRudd>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <JarrowRudd>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); method = "Binomial Cox-Ross-Rubinstein"; europeanOption.setPricingEngine(new BinomialVanillaEngine <CoxRossRubinstein>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <CoxRossRubinstein>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <CoxRossRubinstein>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Additive equiprobabilities method = "Additive equiprobabilities"; europeanOption.setPricingEngine(new BinomialVanillaEngine <AdditiveEQPBinomialTree>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <AdditiveEQPBinomialTree>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <AdditiveEQPBinomialTree>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Trigeorgis method = "Binomial Trigeorgis"; europeanOption.setPricingEngine(new BinomialVanillaEngine <Trigeorgis>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <Trigeorgis>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <Trigeorgis>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Tian method = "Binomial Tian"; europeanOption.setPricingEngine(new BinomialVanillaEngine <Tian>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <Tian>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <Tian>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Leisen-Reimer method = "Binomial Leisen-Reimer"; europeanOption.setPricingEngine(new BinomialVanillaEngine <LeisenReimer>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <LeisenReimer>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <LeisenReimer>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Binomial method: Binomial Joshi method = "Binomial Joshi"; europeanOption.setPricingEngine(new BinomialVanillaEngine <Joshi4>(bsmProcess, timeSteps)); bermudanOption.setPricingEngine(new BinomialVanillaEngine <Joshi4>(bsmProcess, timeSteps)); americanOption.setPricingEngine(new BinomialVanillaEngine <Joshi4>(bsmProcess, timeSteps)); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", bermudanOption.NPV()); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // Monte Carlo Method: MC (crude) timeSteps = 1; method = "MC (crude)"; ulong mcSeed = 42; IPricingEngine mcengine1 = new MakeMCEuropeanEngine <PseudoRandom>(bsmProcess) .withSteps(timeSteps) .withAbsoluteTolerance(0.02) .withSeed(mcSeed) .value(); europeanOption.setPricingEngine(mcengine1); // Real errorEstimate = europeanOption.errorEstimate(); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", "N/A"); // Monte Carlo Method: QMC (Sobol) method = "QMC (Sobol)"; int nSamples = 32768; // 2^15 IPricingEngine mcengine2 = new MakeMCEuropeanEngine <LowDiscrepancy>(bsmProcess) .withSteps(timeSteps) .withSamples(nSamples) .value(); europeanOption.setPricingEngine(mcengine2); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", europeanOption.NPV()); Console.Write("{0,-" + widths[2] + ":0.000000}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", "N/A"); // Monte Carlo Method: MC (Longstaff Schwartz) method = "MC (Longstaff Schwartz)"; IPricingEngine mcengine3 = new MakeMCAmericanEngine <PseudoRandom>(bsmProcess) .withSteps(100) .withAntitheticVariate() .withCalibrationSamples(4096) .withAbsoluteTolerance(0.02) .withSeed(mcSeed) .value(); americanOption.setPricingEngine(mcengine3); Console.Write("{0,-" + widths[0] + "}", method); Console.Write("{0,-" + widths[1] + ":0.000000}", "N/A"); Console.Write("{0,-" + widths[2] + ":0.000000}", "N/A"); Console.WriteLine("{0,-" + widths[3] + ":0.000000}", americanOption.NPV()); // End test Console.WriteLine(" \nRun completed in {0}", DateTime.Now - timer); Console.WriteLine(); Console.Write("Press any key to continue ..."); Console.ReadKey(); }
public void testCashAtHitOrNothingAmericanGreeks() { // Testing American cash-(at-hit)-or-nothing digital option greeks using (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); } } } } } } } } } } } }
static void Main(string[] args) { const int xSteps = 100; const int tSteps = 25; const int dampingSteps = 0; Date today = new Date(15, Month.January, 2020); Settings.instance().setEvaluationDate(today); DayCounter dc = new Actual365Fixed(); YieldTermStructureHandle rTS = new YieldTermStructureHandle( new FlatForward(today, 0.06, dc)); YieldTermStructureHandle qTS = new YieldTermStructureHandle( new FlatForward(today, 0.02, dc)); const double strike = 110.0; StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Put, strike); Date maturityDate = today.Add(new Period(1, TimeUnit.Years)); double maturity = dc.yearFraction(today, maturityDate); Exercise exercise = new AmericanExercise(today, maturityDate); Instrument vanillaOption = new VanillaOption(payoff, exercise); QuoteHandle spot = new QuoteHandle(new SimpleQuote(100.0)); BlackVolTermStructureHandle volatility = new BlackVolTermStructureHandle( new BlackConstantVol(today, new TARGET(), 0.20, dc)); BlackScholesMertonProcess process = new BlackScholesMertonProcess(spot, qTS, rTS, volatility); vanillaOption.setPricingEngine(new FdBlackScholesVanillaEngine( process, tSteps, xSteps, dampingSteps)); double expected = vanillaOption.NPV(); // build an PDE engine from scratch Fdm1dMesher equityMesher = new FdmBlackScholesMesher( xSteps, process, maturity, strike, nullDouble(), nullDouble(), 0.0001, 1.5, new DoublePair(strike, 0.1)); FdmMesherComposite mesher = new FdmMesherComposite(equityMesher); FdmLinearOpComposite op = new FdmBlackScholesOp(mesher, process, strike); FdmInnerValueCalculator calc = new FdmLogInnerValue(payoff, mesher, 0); QlArray x = new QlArray(equityMesher.size()); QlArray rhs = new QlArray(equityMesher.size()); FdmLinearOpIterator iter = mesher.layout().begin(); for (uint i = 0; i < rhs.size(); ++i, iter.increment()) { x.set(i, mesher.location(iter, 0)); rhs.set(i, calc.avgInnerValue(iter, maturity)); } FdmBoundaryConditionSet bcSet = new FdmBoundaryConditionSet(); FdmStepConditionComposite stepCondition = FdmStepConditionComposite.vanillaComposite( new DividendSchedule(), exercise, mesher, calc, today, dc); FdmLinearOpComposite proxyOp = new FdmLinearOpCompositeProxy( new FdmBSDelegate(op)); FdmBackwardSolver solver = new FdmBackwardSolver( proxyOp, bcSet, stepCondition, FdmSchemeDesc.Douglas()); solver.rollback(rhs, maturity, 0.0, tSteps, dampingSteps); double logS = Math.Log(spot.value()); double calculated = new CubicNaturalSpline(x, rhs).call(logS); Console.WriteLine("Homebrew PDE engine : {0:0.0000}", calculated); Console.WriteLine("FdBlackScholesVanillaEngine: {0:0.0000}", expected); }