Package com.opengamma.analytics.financial.model.option.pricing.tree

Source Code of com.opengamma.analytics.financial.model.option.pricing.tree.EuropeanSingleBarrierOptionFunctionProviderTest

/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.model.option.pricing.tree;

import static org.testng.Assert.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertTrue;

import org.apache.commons.lang.NotImplementedException;
import org.testng.annotations.Test;

import com.opengamma.analytics.financial.greeks.Greek;
import com.opengamma.analytics.financial.greeks.GreekResultCollection;
import com.opengamma.analytics.financial.model.volatility.BlackScholesFormulaRepository;
import com.opengamma.analytics.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;

/**
*
*/
public class EuropeanSingleBarrierOptionFunctionProviderTest {
  private static final ProbabilityDistribution<Double> NORMAL = new NormalDistribution(0, 1);

  private static final BinomialTreeOptionPricingModel _model = new BinomialTreeOptionPricingModel();
  private static final TrinomialTreeOptionPricingModel _modelTrinomial = new TrinomialTreeOptionPricingModel();
  private static final double SPOT = 105.;
  private static final double[] STRIKES = new double[] {97., 105., 114. };
  private static final double TIME = 4.2;
  private static final double[] INTERESTS = new double[] {0.015, 0.05 };
  private static final double[] DIVIDENDS = new double[] {0.005, 0.02 };

  /**
   *
   */
  @Test
  public void priceTrinomialTest() {
    final LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
    final double[] vols = new double[] {0.02 };

    final int nSteps = 3121;
    final double[] barrierSet = new double[] {90., 112. };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };
    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        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) {
                  final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                      EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                  double exact = price(SPOT, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double res = _modelTrinomial.getPrice(lattice, function, SPOT, vol, interest, dividend);
                  assertEquals(res, exact, Math.max(exact, 1.) * 1.e-2);
                }
              }
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void greeksTrinomialTest() {
    final double eps = 1.e-6;
    final LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
    final double[] vols = new double[] {0.02 };

    final int nSteps = 3121;
    final double[] barrierSet = new double[] {90., 112. };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };
    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        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) {
                  final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                      EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                  final double price = price(SPOT, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpotUp = price(SPOT + eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpotDown = price(SPOT - eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpot2Up = price(SPOT + 2. * eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpot2Down = price(SPOT - 2. * eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceTimeUp = price(SPOT, strike, TIME + eps, vol, interest, dividend, isCall, barrier, type);
                  final double priceTimeDown = price(SPOT, strike, TIME - eps, vol, interest, dividend, isCall, barrier, type);
                  final double delta = 0.5 * (priceSpotUp - priceSpotDown) / eps;
                  final double deltaUp = 0.5 * (priceSpot2Up - price) / eps;
                  final double deltaDown = 0.5 * (price - priceSpot2Down) / eps;
                  final double gamma = 0.5 * (deltaUp - deltaDown) / eps;
                  final double theta = -0.5 * (priceTimeUp - priceTimeDown) / eps;
                  final GreekResultCollection res = _modelTrinomial.getGreeks(lattice, function, SPOT, vol, interest, dividend);
                  assertEquals(res.get(Greek.FAIR_PRICE), price, Math.max(price, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-1);
                  assertEquals(res.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-1);
                }
              }
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void priceTest() {
    /*
     * Due to slow convergence, only one lattice is used in this test
     */
    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();

    /*
     *  As expected, large vol and spot \sim barrier leads to poor accuracy since the effect of discreteness becomes large.
     */
    final double[] vols = new double[] {0.02 };

    final int nSteps = 3121;
    final double[] barrierSet = new double[] {90., 112. };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };
    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        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) {
                  final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                      EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                  double exact = price(SPOT, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
                  assertEquals(res, exact, Math.max(exact, 1.) * 1.e-2);
                }
              }
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void greeksTest() {
    final double eps = 1.e-6;
    /*
     * Due to slow convergence, only one lattice is used in this test
     */
    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();

    /*
     *  As expected, large vol and spot \sim barrier leads to poor accuracy since the effect of discreteness becomes large.
     */
    final double[] vols = new double[] {0.02 };

    final int nSteps = 3121;

    final double[] barrierSet = new double[] {90, 112 };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };
    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        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) {
                  final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                      EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                  final double price = price(SPOT, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpotUp = price(SPOT + eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpotDown = price(SPOT - eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpot2Up = price(SPOT + 2. * eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceSpot2Down = price(SPOT - 2. * eps, strike, TIME, vol, interest, dividend, isCall, barrier, type);
                  final double priceTimeUp = price(SPOT, strike, TIME + eps, vol, interest, dividend, isCall, barrier, type);
                  final double priceTimeDown = price(SPOT, strike, TIME - eps, vol, interest, dividend, isCall, barrier, type);
                  final double delta = 0.5 * (priceSpotUp - priceSpotDown) / eps;
                  final double deltaUp = 0.5 * (priceSpot2Up - price) / eps;
                  final double deltaDown = 0.5 * (price - priceSpot2Down) / eps;
                  final double gamma = 0.5 * (deltaUp - deltaDown) / eps;
                  final double theta = -0.5 * (priceTimeUp - priceTimeDown) / eps;
                  final GreekResultCollection res = _model.getGreeks(lattice, function, SPOT, vol, interest, dividend);
                  assertEquals(res.get(Greek.FAIR_PRICE), price, Math.max(price, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-1);
                  assertEquals(res.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-1);
                }
              }
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void discreteDividendPriceTest() {
    /*
     * Due to slow convergence, only one lattice is used in this test
     */
    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();

    final double[] propDividends = new double[] {0.002, 0.001, 0.002 };
    final double[] cashDividends = new double[] {.2, 1.1, .5 };
    final double[] dividendTimes = new double[] {TIME / 6., TIME / 3., TIME / 2. };

    final double[] vols = new double[] {0.02 };
    final double[] barrierSet = new double[] {90, 112 };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };

    final int nSteps = 3121;

    final LatticeSpecification latticeTri = new TianLatticeSpecification();
    final int nStepsTri = 121;

    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        for (final boolean isCall : tfSet) {
          for (final double strike : STRIKES) {
            for (final double interest : INTERESTS) {
              for (final double vol : vols) {
                final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                    EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));

                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 DividendFunctionProvider cashDividend = new CashDividendFunctionProvider(dividendTimes, cashDividends);
                final DividendFunctionProvider propDividend = new ProportionalDividendFunctionProvider(dividendTimes, propDividends);
                final double resMod = _model.getPrice(lattice, function, SPOT, vol, interest, cashDividend);
                final double resRes = _model.getPrice(lattice, function, SPOT, vol, interest, propDividend);

                double exactMod = price(modSpot, strike, TIME, vol, interest, 0., isCall, barrier, type);
                assertEquals(resMod, exactMod, Math.max(exactMod, 1.) * 1.e-2);
                double exactRes = price(resSpot, strike, TIME, vol, interest, 0., isCall, barrier, type);
                assertEquals(resRes, exactRes, Math.max(exactRes, 1.) * 1.e-2);

                final OptionFunctionProvider1D functionTri = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nStepsTri, isCall, barrier,
                    EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                final double resTriCash = _modelTrinomial.getPrice(latticeTri, functionTri, SPOT, vol, interest, cashDividend);
                final double resTriProp = _modelTrinomial.getPrice(latticeTri, functionTri, SPOT, vol, interest, propDividend);
                assertEquals(resTriCash, exactMod, Math.max(exactMod, 1.) * 1.e-1);
                assertEquals(resTriProp, exactRes, Math.max(exactRes, 1.) * 1.e-1);
              }
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void discreteDividendGreeksTest() {
    final double eps = 1.e-6;
    /*
     * Due to slow convergence, only one lattice is used in this test
     */
    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();
    final int nSteps = 3121;

    final double[] propDividends = new double[] {0.002, 0.001, 0.002 };
    final double[] cashDividends = new double[] {.2, 1.1, .5 };
    final double[] dividendTimes = new double[] {TIME / 6., TIME / 3., TIME / 2. };

    final double[] vols = new double[] {0.02 };
    final double[] barrierSet = new double[] {90, 112 };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };

    final LatticeSpecification latticeTri = new TianLatticeSpecification();
    final int nStepsTri = 2121;

    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        for (final boolean isCall : tfSet) {
          for (final double strike : STRIKES) {
            for (final double interest : INTERESTS) {
              for (final double vol : vols) {
                final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                    EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                final OptionFunctionProvider1D functionTri = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nStepsTri, isCall, barrier,
                    EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));

                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 DividendFunctionProvider cashDividend = new CashDividendFunctionProvider(dividendTimes, cashDividends);
                final DividendFunctionProvider propDividend = new ProportionalDividendFunctionProvider(dividendTimes, propDividends);
                {
                  final double price = price(modSpot, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpotUp = price(modSpot + eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpotDown = price(modSpot - eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpot2Up = price(modSpot + 2. * eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpot2Down = price(modSpot - 2. * eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceTimeUp = price(modSpot, strike, TIME + eps, vol, interest, 0., isCall, barrier, type);
                  final double priceTimeDown = price(modSpot, strike, TIME - eps, vol, interest, 0., isCall, barrier, type);
                  final double delta = 0.5 * (priceSpotUp - priceSpotDown) / eps;
                  final double deltaUp = 0.5 * (priceSpot2Up - price) / eps;
                  final double deltaDown = 0.5 * (price - priceSpot2Down) / eps;
                  final double gamma = 0.5 * (deltaUp - deltaDown) / eps;
                  final double theta = -0.5 * (priceTimeUp - priceTimeDown) / eps;
                  final GreekResultCollection res = _model.getGreeks(lattice, function, SPOT, vol, interest, cashDividend);
                  assertEquals(res.get(Greek.FAIR_PRICE), price, Math.max(price, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-1);
                  assertEquals(res.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-1);
                  assertEquals(res.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-1);

                  final GreekResultCollection resTri = _modelTrinomial.getGreeks(latticeTri, functionTri, SPOT, vol, interest, cashDividend);
                  assertEquals(resTri.get(Greek.FAIR_PRICE), price, Math.max(price, 1.) * 1.e-2);
                  assertEquals(resTri.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-1);
                  assertEquals(resTri.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-1);
                  assertEquals(resTri.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-1);
                }
                {
                  final double price = price(resSpot, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpotUp = price(resSpot + eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpotDown = price(resSpot - eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpot2Up = price(resSpot + 2. * eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceSpot2Down = price(resSpot - 2. * eps, strike, TIME, vol, interest, 0., isCall, barrier, type);
                  final double priceTimeUp = price(resSpot, strike, TIME + eps, vol, interest, 0., isCall, barrier, type);
                  final double priceTimeDown = price(resSpot, strike, TIME - eps, vol, interest, 0., isCall, barrier, type);
                  final double delta = 0.5 * (priceSpotUp - priceSpotDown) / eps;
                  final double deltaUp = 0.5 * (priceSpot2Up - price) / eps;
                  final double deltaDown = 0.5 * (price - priceSpot2Down) / eps;
                  final double gamma = 0.5 * (deltaUp - deltaDown) / eps;
                  final double theta = -0.5 * (priceTimeUp - priceTimeDown) / eps;
                  final GreekResultCollection res = _model.getGreeks(lattice, function, SPOT, vol, interest, propDividend);
                  assertEquals(res.get(Greek.FAIR_PRICE), price, Math.max(price, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-2);
                  assertEquals(res.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-1);
                  assertEquals(res.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-1);

                  final GreekResultCollection resTri = _modelTrinomial.getGreeks(latticeTri, functionTri, SPOT, vol, interest, propDividend);
                  assertEquals(resTri.get(Greek.FAIR_PRICE), price, Math.max(price, 1.) * 1.e-1);
                  assertEquals(resTri.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-1);
                  assertEquals(resTri.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-1);
                  assertEquals(resTri.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-1);
                }
              }
            }
          }
        }
      }
    }
  }

  /**
   * non-constant volatility and interest rate
   */
  @Test
  public void timeVaryingVolTest() {
    final LatticeSpecification lattice1 = new TimeVaryingLatticeSpecification();
    final double[] time_set = new double[] {0.5, 1.2 };
    final int steps = 801;

    final double[] vol = new double[steps];
    final double[] rate = new double[steps];
    final double[] dividend = new double[steps];
    final int stepsTri = 691;
    final double[] volTri = new double[stepsTri];
    final double[] rateTri = new double[stepsTri];
    final double[] dividendTri = new double[stepsTri];
    final double constA = 0.01;
    final double constB = 0.001;
    final double constC = 0.1;
    final double constD = 0.05;

    final boolean[] tfSet = new boolean[] {true, false };
    for (final boolean isCall : tfSet) {
      for (final double strike : STRIKES) {
        for (final double time : time_set) {
          for (int i = 0; i < steps; ++i) {
            rate[i] = constA + constB * i * time / steps;
            vol[i] = constC + constD * Math.sin(i * time / steps);
            dividend[i] = 0.005;
          }
          for (int i = 0; i < stepsTri; ++i) {
            rateTri[i] = constA + constB * i * time / steps;
            volTri[i] = constC + constD * Math.sin(i * time / steps);
            dividendTri[i] = 0.005;
          }
          final double rateRef = constA + 0.5 * constB * time;
          final double volRef = Math.sqrt(constC * constC + 0.5 * constD * constD + 2. * constC * constD / time * (1. - Math.cos(time)) - constD * constD * 0.25 / time * Math.sin(2. * time));

          final double[] barrierSet = new double[] {SPOT * 0.9, SPOT * 1.1 };
          for (final double barrier : barrierSet) {
            final OptionFunctionProvider1D functionBarrierDown = new EuropeanSingleBarrierOptionFunctionProvider(strike, time, steps, isCall, barrier,
                EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.DownAndOut);
            final OptionFunctionProvider1D functionBarrierUp = new EuropeanSingleBarrierOptionFunctionProvider(strike, time, steps, isCall, barrier,
                EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.UpAndOut);

            final double resPriceBarrierDown = _model.getPrice(functionBarrierDown, SPOT, vol, rate, dividend);
            final GreekResultCollection resGreeksBarrierDown = _model.getGreeks(functionBarrierDown, SPOT, vol, rate, dividend);
            final double resPriceConstBarrierDown = _model.getPrice(lattice1, functionBarrierDown, SPOT, volRef, rateRef, dividend[0]);
            final GreekResultCollection resGreeksConstBarrierDown = _model.getGreeks(lattice1, functionBarrierDown, SPOT, volRef, rateRef, dividend[0]);
            assertEquals(resPriceBarrierDown, resPriceConstBarrierDown, Math.max(Math.abs(resPriceConstBarrierDown), 0.1) * 1.e-1);
            assertEquals(resGreeksBarrierDown.get(Greek.FAIR_PRICE), resGreeksConstBarrierDown.get(Greek.FAIR_PRICE), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.FAIR_PRICE)), 0.1) * 0.1);
            assertEquals(resGreeksBarrierDown.get(Greek.DELTA), resGreeksConstBarrierDown.get(Greek.DELTA), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.DELTA)), 0.1) * 0.1);
            assertEquals(resGreeksBarrierDown.get(Greek.GAMMA), resGreeksConstBarrierDown.get(Greek.GAMMA), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.GAMMA)), 0.1) * 0.1);
            assertEquals(resGreeksBarrierDown.get(Greek.THETA), resGreeksConstBarrierDown.get(Greek.THETA), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.THETA)), 0.1));

            final double resPriceBarrierUp = _model.getPrice(functionBarrierUp, SPOT, vol, rate, dividend);
            final GreekResultCollection resGreeksBarrierUp = _model.getGreeks(functionBarrierUp, SPOT, vol, rate, dividend);
            final double resPriceConstBarrierUp = _model.getPrice(lattice1, functionBarrierUp, SPOT, volRef, rateRef, dividend[0]);
            final GreekResultCollection resGreeksConstBarrierUp = _model.getGreeks(lattice1, functionBarrierUp, SPOT, volRef, rateRef, dividend[0]);
            assertEquals(resPriceBarrierUp, resPriceConstBarrierUp, Math.max(Math.abs(resPriceConstBarrierUp), 0.1) * 1.e-1);
            assertEquals(resGreeksBarrierUp.get(Greek.FAIR_PRICE), resGreeksConstBarrierUp.get(Greek.FAIR_PRICE), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.FAIR_PRICE)), 0.1) * 0.1);
            assertEquals(resGreeksBarrierUp.get(Greek.DELTA), resGreeksConstBarrierUp.get(Greek.DELTA), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.DELTA)), 0.1) * 0.1);
            assertEquals(resGreeksBarrierUp.get(Greek.GAMMA), resGreeksConstBarrierUp.get(Greek.GAMMA), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.GAMMA)), 0.1) * 0.1);
            assertEquals(resGreeksBarrierUp.get(Greek.THETA), resGreeksConstBarrierUp.get(Greek.THETA), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.THETA)), 0.1));

            final OptionFunctionProvider1D functionBarrierDownTri = new EuropeanSingleBarrierOptionFunctionProvider(strike, time, stepsTri, isCall, barrier,
                EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.DownAndOut);
            final OptionFunctionProvider1D functionBarrierUpTri = new EuropeanSingleBarrierOptionFunctionProvider(strike, time, stepsTri, isCall, barrier,
                EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.UpAndOut);

            final double resPriceBarrierDownTri = _modelTrinomial.getPrice(functionBarrierDownTri, SPOT, volTri, rateTri, dividendTri);
            final GreekResultCollection resGreeksBarrierDownTri = _modelTrinomial.getGreeks(functionBarrierDownTri, SPOT, volTri, rateTri, dividendTri);
            assertEquals(resPriceBarrierDownTri, resPriceConstBarrierDown, Math.max(Math.abs(resPriceConstBarrierDown), 1.) * 1.e-1);
            assertEquals(resGreeksBarrierDownTri.get(Greek.FAIR_PRICE), resGreeksConstBarrierDown.get(Greek.FAIR_PRICE), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.FAIR_PRICE)), 1.) * 0.1);
            assertEquals(resGreeksBarrierDownTri.get(Greek.DELTA), resGreeksConstBarrierDown.get(Greek.DELTA), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.DELTA)), 1.) * 0.1);
            assertEquals(resGreeksBarrierDownTri.get(Greek.GAMMA), resGreeksConstBarrierDown.get(Greek.GAMMA), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.GAMMA)), 1.) * 0.1);
            assertEquals(resGreeksBarrierDownTri.get(Greek.THETA), resGreeksConstBarrierDown.get(Greek.THETA), Math.max(Math.abs(resGreeksConstBarrierDown.get(Greek.THETA)), 1.));

            final double resPriceBarrierUpTri = _modelTrinomial.getPrice(functionBarrierUpTri, SPOT, volTri, rateTri, dividendTri);
            final GreekResultCollection resGreeksBarrierUpTri = _modelTrinomial.getGreeks(functionBarrierUpTri, SPOT, volTri, rateTri, dividendTri);
            assertEquals(resPriceBarrierUpTri, resPriceConstBarrierUp, Math.max(Math.abs(resPriceConstBarrierUp), 1.) * 1.e-1);
            assertEquals(resGreeksBarrierUpTri.get(Greek.FAIR_PRICE), resGreeksConstBarrierUp.get(Greek.FAIR_PRICE), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.FAIR_PRICE)), 1.) * 0.1);
            assertEquals(resGreeksBarrierUpTri.get(Greek.DELTA), resGreeksConstBarrierUp.get(Greek.DELTA), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.DELTA)), 1.) * 0.1);
            assertEquals(resGreeksBarrierUpTri.get(Greek.GAMMA), resGreeksConstBarrierUp.get(Greek.GAMMA), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.GAMMA)), 1.) * 0.1);
            assertEquals(resGreeksBarrierUpTri.get(Greek.THETA), resGreeksConstBarrierUp.get(Greek.THETA), Math.max(Math.abs(resGreeksConstBarrierUp.get(Greek.THETA)), 1.));
          }

        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void getBarrierTest() {
    final EuropeanSingleBarrierOptionFunctionProvider function = new EuropeanSingleBarrierOptionFunctionProvider(STRIKES[2], 1., 101, true, 90.,
        EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DownAndOut"));
    assertEquals(function.getBarrier(), 90.);
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(expectedExceptions = IllegalArgumentException.class)
  public void negativeBarrierTest() {
    new EuropeanSingleBarrierOptionFunctionProvider(STRIKES[2], 1., 101, true, -2., EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DownAndOut"));
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(expectedExceptions = NotImplementedException.class)
  public void downInBarrierTest() {
    new EuropeanSingleBarrierOptionFunctionProvider(STRIKES[2], 1., 101, true, 90., EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DownAndIn"));
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(expectedExceptions = NotImplementedException.class)
  public void upInBarrierTest() {
    new EuropeanSingleBarrierOptionFunctionProvider(STRIKES[2], 1., 101, true, 90., EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("UpAndIn"));
  }

  /**
   *
   */
  @Test
  public void hashCodeEqualsTest() {
    final EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes type = EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DownAndOut");
    final OptionFunctionProvider1D ref = new EuropeanSingleBarrierOptionFunctionProvider(100., 1., 53, true, 90., type);
    final OptionFunctionProvider1D[] function = new OptionFunctionProvider1D[] {ref, new EuropeanSingleBarrierOptionFunctionProvider(100., 1., 53, true, 90., type),
        new EuropeanSingleBarrierOptionFunctionProvider(100., 1., 53, true, 91., type),
        new EuropeanSingleBarrierOptionFunctionProvider(100., 1., 53, true, 90., EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("UpAndOut")),
        new EuropeanVanillaOptionFunctionProvider(100., 1., 53, true), null };
    final int len = function.length;
    for (int i = 0; i < len; ++i) {
      if (ref.equals(function[i])) {
        assertTrue(ref.hashCode() == function[i].hashCode());
      }
    }
    for (int i = 0; i < len - 1; ++i) {
      assertTrue(function[i].equals(ref) == ref.equals(function[i]));
    }
    assertFalse(ref.equals(new EuropeanSpreadOptionFunctionProvider(100., 1., 53, true)));
  }

  private double price(final double spot, final double strike, final double time, final double vol, final double interest, final double dividend, final boolean isCall, final double barrier,
      final String type) {
    double exact = 0.;
    if (type == "DownAndOut") {
      if (strike > barrier) {
        exact = isCall ? getA(spot, strike, time, vol, interest, dividend, 1.) - getC(spot, strike, time, vol, interest, dividend, barrier, 1., 1.) : getA(
            spot, strike, time, vol, interest, dividend, -1.) - getB(spot, strike, time, vol, interest, dividend, barrier, -1.) +
            getC(spot, strike, time, vol, interest, dividend, barrier, -1., 1.) - getD(spot, strike, time, vol, interest, dividend, barrier, -1., 1.);
        exact = exact < 0. ? 0. : exact;
        exact = spot <= barrier ? 0. : exact;
      } else {
        exact = isCall ? getB(spot, strike, time, vol, interest, dividend, barrier, 1.) - getD(spot, strike, time, vol, interest, dividend, barrier, 1., 1.) : 0.;
        exact = exact < 0. ? 0. : exact;
        exact = spot <= barrier ? 0. : exact;
      }
    } else {
      if (strike < barrier) {
        exact = !isCall ? getA(spot, strike, time, vol, interest, dividend, -1.) - getC(spot, strike, time, vol, interest, dividend, barrier, -1., -1.) : getA(
            spot, strike, time, vol, interest, dividend, 1.) - getB(spot, strike, time, vol, interest, dividend, barrier, 1.) +
            getC(spot, strike, time, vol, interest, dividend, barrier, 1., -1.) - getD(spot, strike, time, vol, interest, dividend, barrier, 1., -1.);
        exact = exact < 0. ? 0. : exact;
        exact = spot >= barrier ? 0. : exact;
      } else {
        exact = !isCall ? getB(spot, strike, time, vol, interest, dividend, barrier, -1.) - getD(spot, strike, time, vol, interest, dividend, barrier, -1., -1.) : 0.;
        exact = exact < 0. ? 0. : exact;
        exact = spot >= barrier ? 0. : exact;
      }
    }
    return exact;
  }

  /*
   * Tests below are for debugging
   */

  /**
   * Showing slow convergence for non-small vol
   */
  @Test(enabled = false)
  public void price1Test() {

    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();
    final double vol = 0.2;

    final int nSteps = 298121;
    final double barrier = 85;
    String type = "DownAndOut";
    final boolean isCall = true;
    final double strike = 110.;
    final double interest = 0.08;
    final double dividend = 0.02;
    final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
        EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
    double exact = price(SPOT, strike, TIME, vol, interest, dividend, isCall, barrier, type);
    final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
    assertEquals(res, exact, Math.max(exact, 1.) * 1.e-3);
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(enabled = false)
  public void printTest() {
    final double barrier = 90.0;
    final double strike = 105.1;
    final double vol = 0.09;
    final double interest = -0.01;
    final double dividend = 0.0;
    final boolean isCall = false;

    for (int i = 0; i < 500; ++i) {
      final int nSteps = 2001 + 6 * i;
      final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
          EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DownAndOut"));
      //      final LatticeSpecification lattice = new TrigeorgisLatticeSpecification();
      //      final LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification();
      final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();
      double exact = isCall ? getA(SPOT, strike, TIME, vol, interest, dividend, 1.) - getC(SPOT, strike, TIME, vol, interest, dividend, barrier, 1., 1.) : getA(
          SPOT, strike, TIME, vol, interest, dividend, -1.) -
          getB(SPOT, strike, TIME, vol, interest, dividend, barrier, -1.) +
          getC(SPOT, strike, TIME, vol, interest, dividend, barrier, -1., 1.) -
          getD(SPOT, strike, TIME, vol, interest, dividend, barrier, -1., 1.);
      exact = exact < 0. ? 0. : exact;
      exact = SPOT <= barrier ? 0. : exact;
      final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
      System.out.println(nSteps + "\t" + (res - exact));
    }
  }

  /**
   *
   */
  @Test(enabled = false)
  public void priceLeisenReimerTest() {
    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();
    /*
     *  As expected, large vol and spot \sim barrier leads to poor accuracy since the effect of discreteness becomes large.
     */
    final double[] vols = new double[] {0.02, 0.09 };
    final double eps = 1.e-2;
    final int nSteps = 1189;

    final double[] barrierSet = new double[] {90, 121 };
    final String[] typeSet = new String[] {"DownAndOut", "UpAndOut" };
    final boolean[] tfSet = new boolean[] {true, false };
    for (final double barrier : barrierSet) {
      for (final String type : typeSet) {
        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) {
                  final OptionFunctionProvider1D function = new EuropeanSingleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, barrier,
                      EuropeanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
                  if (type == "DownAndOut") {
                    if (strike > barrier) {
                      double exact = isCall ? getA(SPOT, strike, TIME, vol, interest, dividend, 1.) - getC(SPOT, strike, TIME, vol, interest, dividend, barrier, 1., 1.) : getA(
                          SPOT, strike, TIME, vol, interest, dividend, -1.) -
                          getB(SPOT, strike, TIME, vol, interest, dividend, barrier, -1.) +
                          getC(SPOT, strike, TIME, vol, interest, dividend, barrier, -1., 1.) -
                          getD(SPOT, strike, TIME, vol, interest, dividend, barrier, -1., 1.);
                      exact = exact < 0. ? 0. : exact;
                      exact = SPOT <= barrier ? 0. : exact;
                      final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
                      System.out.println(barrier + "\t" + strike + "\t" + vol + "\t" + interest + "\t" + dividend + "\t" + isCall);
                      assertEquals(res, exact, Math.max(exact, 1.) * eps);
                    } else {
                      double exact = isCall ? getB(SPOT, strike, TIME, vol, interest, dividend, barrier, 1.) - getD(SPOT, strike, TIME, vol, interest, dividend, barrier, 1., 1.) : 0.;
                      exact = exact < 0. ? 0. : exact;
                      exact = SPOT <= barrier ? 0. : exact;
                      final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
                      //                        System.out.println(strike + "\t" + vol + "\t" + interest + "\t" + exact + "\t" + res);
                      assertEquals(res, exact, Math.max(exact, 1.) * eps);
                    }
                  } else {
                    if (strike < barrier) {
                      double exact = !isCall ? getA(SPOT, strike, TIME, vol, interest, dividend, -1.) - getC(SPOT, strike, TIME, vol, interest, dividend, barrier, -1., -1.) : getA(
                          SPOT, strike, TIME, vol, interest, dividend, 1.) -
                          getB(SPOT, strike, TIME, vol, interest, dividend, barrier, 1.) +
                          getC(SPOT, strike, TIME, vol, interest, dividend, barrier, 1., -1.) -
                          getD(SPOT, strike, TIME, vol, interest, dividend, barrier, 1., -1.);
                      exact = exact < 0. ? 0. : exact;
                      exact = SPOT >= barrier ? 0. : exact;
                      final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
                      //                        System.out.println(barrier + "\t" + strike + "\t" + vol + "\t" + interest + "\t" + exact + "\t" + res);
                      assertEquals(res, exact, Math.max(exact, 1.) * eps);
                    } else {
                      double exact = !isCall ? getB(SPOT, strike, TIME, vol, interest, dividend, barrier, -1.) - getD(SPOT, strike, TIME, vol, interest, dividend, barrier, -1., -1.) : 0.;
                      exact = exact < 0. ? 0. : exact;
                      exact = SPOT >= barrier ? 0. : exact;
                      final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
                      //                        System.out.println(strike + "\t" + vol + "\t" + interest + "\t" + exact + "\t" + res);
                      assertEquals(res, exact, Math.max(exact, 1.) * eps);
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }

  private double getA(final double spot, final double strike, final double time, final double vol, final double interest, final double dividend, final double phi) {
    final boolean isCall = (phi == 1.);
    return BlackScholesFormulaRepository.price(spot, strike, time, vol, interest, interest - dividend, isCall);
  }

  private double getB(final double spot, final double strike, final double time, final double vol, final double interest, final double dividend, final double barrier, final double phi) {
    final double sigmaRootT = vol * Math.sqrt(time);
    final double x2 = (Math.log(spot / barrier) + interest * time - dividend * time) / sigmaRootT + 0.5 * sigmaRootT;
    final double x2M = x2 - sigmaRootT;
    return phi * (spot * Math.exp(-dividend * time) * NORMAL.getCDF(phi * x2) - strike * Math.exp(-interest * time) * NORMAL.getCDF(phi * x2M));
  }

  private double getC(final double spot, final double strike, final double time, final double vol, final double interest, final double dividend, final double barrier, final double phi,
      final double eta) {
    final boolean isCall = (eta == 1.);
    final double mu = (interest - dividend) / vol / vol - 0.5;
    return phi * eta * BlackScholesFormulaRepository.price(barrier * barrier / spot, strike, time, vol, interest, interest - dividend, isCall) * Math.pow(barrier / spot, 2. * mu);
  }

  private double getD(final double spot, final double strike, final double time, final double vol, final double interest, final double dividend, final double barrier, final double phi,
      final double eta) {
    final double sigmaRootT = vol * Math.sqrt(time);
    final double y2 = (Math.log(barrier / spot) + interest * time - dividend * time) / sigmaRootT + 0.5 * sigmaRootT;
    final double y2M = y2 - sigmaRootT;
    final double mu = (interest - dividend) / vol / vol - 0.5;
    return phi *
        (spot * Math.exp(-dividend * time) * Math.pow(barrier / spot, 2. * mu + 2.) * NORMAL.getCDF(eta * y2) - strike * Math.exp(-interest * time) * Math.pow(barrier / spot, 2. * mu) *
            NORMAL.getCDF(eta * y2M));
  }
}
TOP

Related Classes of com.opengamma.analytics.financial.model.option.pricing.tree.EuropeanSingleBarrierOptionFunctionProviderTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.