public void testAtmCalcs() { // Testing delta-neutral ATM quotations SavedSettings backup = new SavedSettings(); DeltaData[] values = { new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.Spot, 1.421, 0.997306, 0.992266, 0.1180654, 1.608080, 0.15), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.PaSpot, 1.421, 0.997306, 0.992266, 0.1180654, 1.600545, 0.15), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.Fwd, 1.421, 0.997306, 0.992266, 0.1180654, 1.609029, 0.15), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.PaFwd, 1.421, 0.997306, 0.992266, 0.1180654, 1.601550, 0.15), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.Spot, 122.121, 0.9695434, 0.9872347, 0.0887676, 119.8031, 0.67), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.PaSpot, 122.121, 0.9695434, 0.9872347, 0.0887676, 117.7096, 0.67), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.Fwd, 122.121, 0.9695434, 0.9872347, 0.0887676, 120.0592, 0.67), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.PaFwd, 122.121, 0.9695434, 0.9872347, 0.0887676, 118.0532, 0.67), new DeltaData(Option.Type.Put, DeltaVolQuote.DeltaType.Spot, 3.4582, 0.99979, 0.9250616, 0.3199034, 4.964924, -0.821), new DeltaData(Option.Type.Put, DeltaVolQuote.DeltaType.PaSpot, 3.4582, 0.99979, 0.9250616, 0.3199034, 3.778327, -0.821), new DeltaData(Option.Type.Put, DeltaVolQuote.DeltaType.Fwd, 3.4582, 0.99979, 0.9250616, 0.3199034, 4.51896, -0.821), new DeltaData(Option.Type.Put, DeltaVolQuote.DeltaType.PaFwd, 3.4582, 0.99979, 0.9250616, 0.3199034, 3.65728, -0.821), // Data taken from Castagnas "FX Options and Smile Risk" (Wiley 2009) new DeltaData(Option.Type.Put, DeltaVolQuote.DeltaType.Spot, 103.00, 0.99482, 0.98508, 0.07247845, 97.47, -0.25), new DeltaData(Option.Type.Put, DeltaVolQuote.DeltaType.PaSpot, 103.00, 0.99482, 0.98508, 0.07247845, 97.22, -0.25), // Extreme case: zero vol, ATM Fwd strike new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.Fwd, 103.00, 0.99482, 0.98508, 0.0, 101.0013, 0.5), new DeltaData(Option.Type.Call, DeltaVolQuote.DeltaType.Spot, 103.00, 0.99482, 0.98508, 0.0, 101.0013, 0.99482 * 0.5) }; DeltaVolQuote.DeltaType currDt; double currSpot; double currdDf; double currfDf; double currStdDev; double expected; double calculated; double error; double tolerance = 1.0e-2; // not that small, but sufficient for strikes double currAtmStrike; double currCallDelta; double currPutDelta; double currFwd; for (int i = 0; i < values.Length; i++) { currDt = values[i].dt; currSpot = values[i].spot; currdDf = values[i].dDf; currfDf = values[i].fDf; currStdDev = values[i].stdDev; currFwd = currSpot * currfDf / currdDf; BlackDeltaCalculator myCalc = new BlackDeltaCalculator(Option.Type.Call, currDt, currSpot, currdDf, currfDf, currStdDev); currAtmStrike = myCalc.atmStrike(DeltaVolQuote.AtmType.AtmDeltaNeutral); currCallDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Put); currPutDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Call); expected = 0.0; calculated = currCallDelta + currPutDelta; error = Math.Abs(calculated - expected); if (error > tolerance) { QAssert.Fail("\n Delta neutrality failed for spot delta in Delta Calculator. \n" + "Iteration: " + i + "\n" + "Calculated Delta Sum: " + calculated + "\n" + "Expected Delta Sum: " + expected + "\n" + "Error: " + error); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.Fwd); currAtmStrike = myCalc.atmStrike(DeltaVolQuote.AtmType.AtmDeltaNeutral); currCallDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Put); currPutDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Call); expected = 0.0; calculated = currCallDelta + currPutDelta; error = Math.Abs(calculated - expected); if (error > tolerance) { QAssert.Fail("\n Delta neutrality failed for forward delta in Delta Calculator. \n" + "Iteration: " + i + "\n" + "Calculated Delta Sum: " + calculated + "\n" + "Expected Delta Sum: " + expected + "\n" + "Error: " + error); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaSpot); currAtmStrike = myCalc.atmStrike(DeltaVolQuote.AtmType.AtmDeltaNeutral); currCallDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Put); currPutDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Call); expected = 0.0; calculated = currCallDelta + currPutDelta; error = Math.Abs(calculated - expected); if (error > tolerance) { QAssert.Fail("\n Delta neutrality failed for premium-adjusted spot delta in Delta Calculator. \n" + "Iteration: " + i + "\n" + "Calculated Delta Sum: " + calculated + "\n" + "Expected Delta Sum: " + expected + "\n" + "Error: " + error); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaFwd); currAtmStrike = myCalc.atmStrike(DeltaVolQuote.AtmType.AtmDeltaNeutral); currCallDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Put); currPutDelta = myCalc.deltaFromStrike(currAtmStrike); myCalc.setOptionType(Option.Type.Call); expected = 0.0; calculated = currCallDelta + currPutDelta; error = Math.Abs(calculated - expected); if (error > tolerance) { QAssert.Fail("\n Delta neutrality failed for premium-adjusted forward delta in Delta Calculator. \n" + "Iteration: " + i + "\n" + "Calculated Delta Sum: " + calculated + "\n" + "Expected Delta Sum: " + expected + "\n" + "Error: " + error); } // Test ATM forward Calculations calculated = myCalc.atmStrike(DeltaVolQuote.AtmType.AtmFwd); expected = currFwd; error = Math.Abs(expected - calculated); if (error > tolerance) { QAssert.Fail("\n Atm forward test failed. \n" + "Calculated Value: " + calculated + "\n" + "Expected Value: " + expected + "\n" + "Error: " + error); } // Test ATM 0.50 delta calculations myCalc.setDeltaType(DeltaVolQuote.DeltaType.Fwd); double atmFiftyStrike = myCalc.atmStrike(DeltaVolQuote.AtmType.AtmPutCall50); calculated = Math.Abs(myCalc.deltaFromStrike(atmFiftyStrike)); expected = 0.50; error = Math.Abs(expected - calculated); if (error > tolerance) { QAssert.Fail("\n Atm 0.50 delta strike test failed. \n" + "Iteration:" + i + "\n" + "Calculated Value: " + calculated + "\n" + "Expected Value: " + expected + "\n" + "Error: " + error); } } }
public void testPutCallParity() { // Testing put-call parity for deltas // Test for put call parity between put and call deltas. SavedSettings backup = new SavedSettings(); /* The data below are from * "Option pricing formulas", E.G. Haug, McGraw-Hill 1998 * pag 11-16 */ EuropeanOptionData[] values = { // pag 2-8 // type, strike, spot, q, r, t, vol, value, tol new EuropeanOptionData(Option.Type.Call, 65.00, 60.00, 0.00, 0.08, 0.25, 0.30, 2.1334, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 95.00, 100.00, 0.05, 0.10, 0.50, 0.20, 2.4648, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 19.00, 19.00, 0.10, 0.10, 0.75, 0.28, 1.7011, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 19.00, 19.00, 0.10, 0.10, 0.75, 0.28, 1.7011, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 1.60, 1.56, 0.08, 0.06, 0.50, 0.12, 0.0291, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 70.00, 75.00, 0.05, 0.10, 0.50, 0.35, 4.0870, 1.0e-4), // pag 24 new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 0.0205, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8734, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 9.9413, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 0.3150, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1217, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 10.3556, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 0.9474, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3693, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 11.1381, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 0.8069, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0232, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 10.5769, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 2.7026, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.6997, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 12.7857, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 4.9329, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.3679, 1.0e-4), new EuropeanOptionData(Option.Type.Call, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 15.3086, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.15, 9.9210, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.15, 1.8734, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.15, 0.0408, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.25, 10.2155, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.25, 3.1217, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.25, 0.4551, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.10, 0.35, 10.8479, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.10, 0.35, 4.3693, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.10, 0.35, 1.2376, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.15, 10.3192, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.15, 4.0232, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.15, 1.0646, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.25, 12.2149, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.25, 6.6997, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.25, 3.2734, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 90.00, 0.10, 0.10, 0.50, 0.35, 14.4452, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 100.00, 0.10, 0.10, 0.50, 0.35, 9.3679, 1.0e-4), new EuropeanOptionData(Option.Type.Put, 100.00, 110.00, 0.10, 0.10, 0.50, 0.35, 5.7963, 1.0e-4), // pag 27 new EuropeanOptionData(Option.Type.Call, 40.00, 42.00, 0.08, 0.04, 0.75, 0.35, 5.0975, 1.0e-4) }; DayCounter dc = new Actual360(); Calendar calendar = new TARGET(); Date today = Date.Today; double discFor = 0.0; double discDom = 0.0; double implVol = 0.0; double deltaCall = 0.0; double deltaPut = 0.0; double expectedDiff = 0.0; double calculatedDiff = 0.0; double error = 0.0; double forward = 0.0; SimpleQuote spotQuote = new SimpleQuote(0.0); SimpleQuote qQuote = new SimpleQuote(0.0); Handle <Quote> qHandle = new Handle <Quote>(qQuote); YieldTermStructure qTS = new FlatForward(today, qHandle, dc); SimpleQuote rQuote = new SimpleQuote(0.0); Handle <Quote> rHandle = new Handle <Quote>(qQuote); YieldTermStructure rTS = new FlatForward(today, rHandle, dc); SimpleQuote volQuote = new SimpleQuote(0.0); Handle <Quote> volHandle = new Handle <Quote>(volQuote); BlackVolTermStructure volTS = new BlackConstantVol(today, calendar, volHandle, dc); StrikedTypePayoff payoff; Date exDate; Exercise exercise; double tolerance = 1.0e-10; for (int i = 0; i < values.Length; ++i) { payoff = new PlainVanillaPayoff(Option.Type.Call, values[i].strike); exDate = today + timeToDays(values[i].t); exercise = new EuropeanExercise(exDate); spotQuote.setValue(values[i].s); volQuote.setValue(values[i].v); rQuote.setValue(values[i].r); qQuote.setValue(values[i].q); discDom = rTS.discount(exDate); discFor = qTS.discount(exDate); implVol = Math.Sqrt(volTS.blackVariance(exDate, 0.0)); forward = spotQuote.value() * discFor / discDom; BlackDeltaCalculator myCalc = new BlackDeltaCalculator(Option.Type.Call, DeltaVolQuote.DeltaType.Spot, spotQuote.value(), discDom, discFor, implVol); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = discFor; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for spot delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.Fwd); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = 1.0; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for forward delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaSpot); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = discFor * values[i].strike / forward; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for premium-adjusted spot delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } myCalc.setDeltaType(DeltaVolQuote.DeltaType.PaFwd); deltaCall = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Put); deltaPut = myCalc.deltaFromStrike(values[i].strike); myCalc.setOptionType(Option.Type.Call); expectedDiff = values[i].strike / forward; calculatedDiff = deltaCall - deltaPut; error = Math.Abs(expectedDiff - calculatedDiff); if (error > tolerance) { QAssert.Fail("\n Put-call parity failed for premium-adjusted forward delta. \n" + "Calculated Call Delta: " + deltaCall + "\n" + "Calculated Put Delta: " + deltaPut + "\n" + "Expected Difference: " + expectedDiff + "\n" + "Calculated Difference: " + calculatedDiff); } } }