@Test public void smoothConvergenceTest() { final LatticeSpecification lattice = new FlexibleLatticeSpecification(); final boolean[] tfSet = new boolean[] {true, false}; for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { for (final double dividend : DIVIDENDS) { double prev = SPOT; double diff = 0.; if (interest - dividend > 0.) { for (int i = 0; i < 15; ++i) { final int nSteps = 10 + 50 * i; final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final double exactDiv = BlackScholesFormulaRepository.price( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double resDiv = _model.getPrice(lattice, function, SPOT, vol, interest, dividend); diff = Math.abs(exactDiv - resDiv); assertTrue(diff < prev); prev = diff; } } } } } } } }
@Test public void greekTest() { final LatticeSpecification[] lattices = new LatticeSpecification[] { new CoxRossRubinsteinLatticeSpecification(), new JarrowRuddLatticeSpecification(), new TrigeorgisLatticeSpecification(), new JabbourKraminYoungLatticeSpecification(), new TianLatticeSpecification() }; final boolean[] tfSet = new boolean[] {true, false}; for (final LatticeSpecification lattice : lattices) { for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { final int[] choicesSteps = new int[] {31, 112, 301}; for (final int nSteps : choicesSteps) { for (final double dividend : DIVIDENDS) { final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final GreekResultCollection resDiv = _model.getGreeks(lattice, function, SPOT, vol, interest, dividend); final double priceDiv = BlackScholesFormulaRepository.price( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double refPriceDiv = Math.max(Math.abs(priceDiv), 1.) / Math.sqrt(nSteps); assertEquals(resDiv.get(Greek.FAIR_PRICE), priceDiv, refPriceDiv); final double deltaDiv = BlackScholesFormulaRepository.delta( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double refDeltaDiv = Math.max(Math.abs(deltaDiv), 1.) / Math.sqrt(nSteps); assertEquals(resDiv.get(Greek.DELTA), deltaDiv, refDeltaDiv); final double gammaDiv = BlackScholesFormulaRepository.gamma( SPOT, strike, TIME, vol, interest, interest - dividend); final double refGammaDiv = Math.max(Math.abs(gammaDiv), 1.) / Math.sqrt(nSteps); assertEquals(resDiv.get(Greek.GAMMA), gammaDiv, refGammaDiv); final double thetaDiv = BlackScholesFormulaRepository.theta( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double refThetaDiv = Math.max(Math.abs(thetaDiv), 1.) / Math.sqrt(nSteps); assertEquals(resDiv.get(Greek.THETA), thetaDiv, refThetaDiv); } } } } } } } }
@Test public void ImpliedTreeEuropeanRecoveryTest() { final double interest = 0.06; final YieldAndDiscountCurve yCrv = YieldCurve.from(ConstantDoublesCurve.from(interest)); final double cost = 0.02; final double atmVol = 0.47; final ZonedDateTime date = DateUtils.getUTCDate(2010, 7, 1); final double spot = 100; final Function<Double, Double> smile = new Function<Double, Double>() { @Override public Double evaluate(final Double... tk) { ArgChecker.isTrue(tk.length == 2); final double k = tk[1]; return atmVol + (spot - k) * 0.0005; } }; final StandardOptionDataBundle data = new StandardOptionDataBundle( yCrv, cost, new VolatilitySurface(FunctionalDoublesSurface.from(smile)), spot, date); final double[] strikes = new double[] {spot * 0.9, spot, spot * 1.11}; final int nSteps = 7; final double time = 1.; for (int i = 0; i < strikes.length; ++i) { final double strike = strikes[i]; final boolean isCall = strike >= spot ? true : false; final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, time, nSteps, isCall); final double tree = _modelTrinomial.getPrice(function, data); final double black = BlackScholesFormulaRepository.price( spot, strike * 0.9, time, data.getVolatility(time, strike), interest, cost, isCall); assertEquals(tree, black, black * 0.2); } try { _model.getPrice( new EuropeanVanillaOptionFunctionProvider(strikes[2], time, nSteps, true), data); throw new RuntimeException(); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } }
@Test public void greekLeisenReimerTest() { final LatticeSpecification lattice = new LeisenReimerLatticeSpecification(); final boolean[] tfSet = new boolean[] {true, false}; for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { final int[] choicesSteps = new int[] {31, 109, 301}; for (final int nSteps : choicesSteps) { for (final double dividend : DIVIDENDS) { final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final GreekResultCollection resDiv = _model.getGreeks(lattice, function, SPOT, vol, interest, dividend); final double priceDiv = BlackScholesFormulaRepository.price( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double refPriceDiv = Math.max(Math.abs(priceDiv), 1.) / nSteps; assertEquals(resDiv.get(Greek.FAIR_PRICE), priceDiv, refPriceDiv); final double deltaDiv = BlackScholesFormulaRepository.delta( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double refDeltaDiv = Math.max(Math.abs(deltaDiv), 1.) / nSteps; assertEquals(resDiv.get(Greek.DELTA), deltaDiv, refDeltaDiv); final double gammaDiv = BlackScholesFormulaRepository.gamma( SPOT, strike, TIME, vol, interest, interest - dividend); final double refGammaDiv = Math.max(Math.abs(gammaDiv), 1.) / nSteps; assertEquals(resDiv.get(Greek.GAMMA), gammaDiv, refGammaDiv); final double thetaDiv = BlackScholesFormulaRepository.theta( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double refThetaDiv = Math.max(Math.abs(thetaDiv), 1.) / nSteps; assertEquals(resDiv.get(Greek.THETA), thetaDiv, refThetaDiv * 10.); } } } } } } }
@Test public void priceLatticeTest() { final LatticeSpecification[] lattices = new LatticeSpecification[] { new CoxRossRubinsteinLatticeSpecification(), new JarrowRuddLatticeSpecification(), new TrigeorgisLatticeSpecification(), new JabbourKraminYoungLatticeSpecification(), new TianLatticeSpecification(), new LeisenReimerLatticeSpecification() }; final boolean[] tfSet = new boolean[] {true, false}; for (final LatticeSpecification lattice : lattices) { for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { final int[] choicesSteps = new int[] {31, 115, 301}; for (final int nSteps : choicesSteps) { for (final double dividend : DIVIDENDS) { final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final double exactDiv = BlackScholesFormulaRepository.price( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double resDiv = _model.getPrice(lattice, function, SPOT, vol, interest, dividend); final double refDiv = (lattice instanceof LeisenReimerLatticeSpecification) ? Math.max(exactDiv, 1.) / nSteps / nSteps : Math.max(exactDiv, 1.) / Math.sqrt(nSteps); assertEquals(resDiv, exactDiv, refDiv); } } } } } } } }
@Test public void priceLatticeTrinomialTest() { final LatticeSpecification[] lattices = new LatticeSpecification[] { new CoxRossRubinsteinLatticeSpecification(), new JarrowRuddLatticeSpecification(), new TrigeorgisLatticeSpecification(), new TianLatticeSpecification() }; final boolean[] tfSet = new boolean[] {true, false}; for (final LatticeSpecification lattice : lattices) { for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { final int nSteps = 621; for (final double dividend : DIVIDENDS) { final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final double exactDiv = BlackScholesFormulaRepository.price( SPOT, strike, TIME, vol, interest, interest - dividend, isCall); final double resDiv = _modelTrinomial.getPrice(lattice, function, SPOT, vol, interest, dividend); final double refDiv = lattice instanceof CoxRossRubinsteinLatticeSpecification ? Math.max(exactDiv, 1.) * 1.e-2 : Math.max(exactDiv, 1.) * 1.e-3; assertEquals(resDiv, exactDiv, refDiv); } } } } } } }
/** The dividend is cash or proportional to asset price */ @Test public void greeksDiscreteDividendLatticeTest() { final LatticeSpecification[] lattices = new LatticeSpecification[] { new CoxRossRubinsteinLatticeSpecification(), new JarrowRuddLatticeSpecification(), new TrigeorgisLatticeSpecification(), new JabbourKraminYoungLatticeSpecification(), new TianLatticeSpecification(), new LeisenReimerLatticeSpecification() }; final double[] propDividends = new double[] {0.01, 0.01, 0.01}; final double[] cashDividends = new double[] {5., 10., 8.}; final double[] dividendTimes = new double[] {TIME / 420., TIME / 203., TIME / 2.}; final boolean[] tfSet = new boolean[] {true, false}; for (final LatticeSpecification lattice : lattices) { for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { final int nSteps = 401; final double resSpot = SPOT * (1. - propDividends[0]) * (1. - propDividends[1]) * (1. - propDividends[2]); final double modSpot = SPOT - cashDividends[0] * Math.exp(-interest * dividendTimes[0]) - cashDividends[1] * Math.exp(-interest * dividendTimes[1]) - cashDividends[2] * Math.exp(-interest * dividendTimes[2]); final double exactPriceProp = BlackScholesFormulaRepository.price( resSpot, strike, TIME, vol, interest, interest, isCall); final double exactDeltaProp = BlackScholesFormulaRepository.delta( resSpot, strike, TIME, vol, interest, interest, isCall); final double exactGammaProp = BlackScholesFormulaRepository.gamma( resSpot, strike, TIME, vol, interest, interest); final double exactThetaProp = BlackScholesFormulaRepository.theta( resSpot, strike, TIME, vol, interest, interest, isCall); final double appPriceCash = BlackScholesFormulaRepository.price( modSpot, strike, TIME, vol, interest, interest, isCall); final double appDeltaCash = BlackScholesFormulaRepository.delta( modSpot, strike, TIME, vol, interest, interest, isCall); final double appGammaCash = BlackScholesFormulaRepository.gamma( modSpot, strike, TIME, vol, interest, interest); final double appThetaCash = BlackScholesFormulaRepository.theta( modSpot, strike, TIME, vol, interest, interest, isCall); final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final DividendFunctionProvider cashDividend = new CashDividendFunctionProvider(dividendTimes, cashDividends); final DividendFunctionProvider propDividend = new ProportionalDividendFunctionProvider(dividendTimes, propDividends); final GreekResultCollection resProp = _model.getGreeks(lattice, function, SPOT, vol, interest, propDividend); final GreekResultCollection resCash = _model.getGreeks(lattice, function, SPOT, vol, interest, cashDividend); assertEquals( resProp.get(Greek.FAIR_PRICE), exactPriceProp, Math.max(1., Math.abs(exactPriceProp)) * 1.e-2); assertEquals( resProp.get(Greek.DELTA), exactDeltaProp, Math.max(1., Math.abs(exactDeltaProp)) * 1.e-1); assertEquals( resProp.get(Greek.GAMMA), exactGammaProp, Math.max(1., Math.abs(exactGammaProp)) * 1.e-1); assertEquals( resProp.get(Greek.THETA), exactThetaProp, Math.max(1., Math.abs(exactThetaProp)) * 1.e-1); assertEquals( resCash.get(Greek.FAIR_PRICE), appPriceCash, Math.max(1., Math.abs(appPriceCash)) * 1.e-2); assertEquals( resCash.get(Greek.DELTA), appDeltaCash, Math.max(1., Math.abs(appDeltaCash)) * 1.e-1); assertEquals( resCash.get(Greek.GAMMA), appGammaCash, Math.max(1., Math.abs(appGammaCash)) * 1.e-1); assertEquals( resCash.get(Greek.THETA), appThetaCash, Math.max(1., Math.abs(appThetaCash))); if (lattice instanceof CoxRossRubinsteinLatticeSpecification || lattice instanceof JarrowRuddLatticeSpecification || lattice instanceof TrigeorgisLatticeSpecification || lattice instanceof TianLatticeSpecification) { final GreekResultCollection resPropTrinomial = _modelTrinomial.getGreeks(lattice, function, SPOT, vol, interest, propDividend); final GreekResultCollection resCashTrinomial = _modelTrinomial.getGreeks(lattice, function, SPOT, vol, interest, cashDividend); assertEquals( resPropTrinomial.get(Greek.FAIR_PRICE), resProp.get(Greek.FAIR_PRICE), Math.max(1., Math.abs(resProp.get(Greek.FAIR_PRICE))) * 1.e-2); assertEquals( resPropTrinomial.get(Greek.DELTA), resProp.get(Greek.DELTA), Math.max(1., Math.abs(resProp.get(Greek.DELTA))) * 1.e-2); assertEquals( resPropTrinomial.get(Greek.GAMMA), resProp.get(Greek.GAMMA), Math.max(1., Math.abs(resProp.get(Greek.GAMMA))) * 1.e-2); assertEquals( resPropTrinomial.get(Greek.THETA), resProp.get(Greek.THETA), Math.max(1., Math.abs(resProp.get(Greek.THETA))) * 1.e-1); assertEquals( resCashTrinomial.get(Greek.FAIR_PRICE), resCash.get(Greek.FAIR_PRICE), Math.max(1., Math.abs(resCash.get(Greek.FAIR_PRICE))) * 1.e-2); assertEquals( resCashTrinomial.get(Greek.DELTA), resCash.get(Greek.DELTA), Math.max(1., Math.abs(resCash.get(Greek.DELTA))) * 1.e-2); assertEquals( resCashTrinomial.get(Greek.GAMMA), resCash.get(Greek.GAMMA), Math.max(1., Math.abs(resCash.get(Greek.GAMMA))) * 1.e-2); assertEquals( resCashTrinomial.get(Greek.THETA), resCash.get(Greek.THETA), Math.max(1., Math.abs(resCash.get(Greek.THETA))) * 1.e-1); } } } } } } }
/** The dividend is cash or proportional to asset price */ @Test public void priceDiscreteDividendTest() { final LatticeSpecification[] lattices = new LatticeSpecification[] { new CoxRossRubinsteinLatticeSpecification(), new JarrowRuddLatticeSpecification(), new TrigeorgisLatticeSpecification(), new JabbourKraminYoungLatticeSpecification(), new TianLatticeSpecification(), new LeisenReimerLatticeSpecification() }; final double[] propDividends = new double[] {0.01, 0.01, 0.01}; final double[] cashDividends = new double[] {5., 10., 8.}; final double[] dividendTimes = new double[] {TIME / 9., TIME / 3., TIME / 2.}; final boolean[] tfSet = new boolean[] {true, false}; for (final LatticeSpecification lattice : lattices) { for (final boolean isCall : tfSet) { for (final double strike : STRIKES) { for (final double interest : INTERESTS) { for (final double vol : VOLS) { final int[] choicesSteps = new int[] {33, 115}; for (final int nSteps : choicesSteps) { final OptionFunctionProvider1D function = new EuropeanVanillaOptionFunctionProvider(strike, TIME, nSteps, isCall); final DividendFunctionProvider cashDividend = new CashDividendFunctionProvider(dividendTimes, cashDividends); final DividendFunctionProvider propDividend = new ProportionalDividendFunctionProvider(dividendTimes, propDividends); final double df = Math.exp(-interest * TIME); final double resSpot = SPOT * Math.exp(interest * TIME) * (1. - propDividends[0]) * (1. - propDividends[1]) * (1. - propDividends[2]); final double modSpot = SPOT - cashDividends[0] * Math.exp(-interest * dividendTimes[0]) - cashDividends[1] * Math.exp(-interest * dividendTimes[1]) - cashDividends[2] * Math.exp(-interest * dividendTimes[2]); final double exactProp = df * BlackFormulaRepository.price(resSpot, strike, TIME, vol, isCall); final double appCash = BlackScholesFormulaRepository.price( modSpot, strike, TIME, vol, interest, interest, isCall); final double resProp = _model.getPrice(lattice, function, SPOT, vol, interest, propDividend); final double refProp = Math.max(exactProp, 1.) / Math.sqrt(nSteps); assertEquals(resProp, exactProp, refProp); final double resCash = _model.getPrice(lattice, function, SPOT, vol, interest, cashDividend); final double refCash = Math.max(appCash, 1.) / Math.sqrt(nSteps); assertEquals(resCash, appCash, refCash); if (lattice instanceof CoxRossRubinsteinLatticeSpecification || lattice instanceof JarrowRuddLatticeSpecification || lattice instanceof TrigeorgisLatticeSpecification || lattice instanceof TianLatticeSpecification) { final double resPropTrinomial = _modelTrinomial.getPrice( lattice, function, SPOT, vol, interest, propDividend); final double resCashTrinomial = _modelTrinomial.getPrice( lattice, function, SPOT, vol, interest, cashDividend); assertEquals( resPropTrinomial, resProp, Math.max(resProp, 1.) / Math.sqrt(nSteps)); assertEquals( resCashTrinomial, resCash, Math.max(resCash, 1.) / Math.sqrt(nSteps)); } } } } } } } }