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

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

/**
* 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.math.statistics.distribution.NormalDistribution;
import com.opengamma.analytics.math.statistics.distribution.ProbabilityDistribution;

/**
*
*/
public class DoubleBarrierOptionFunctionProviderTest {
  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[] VOLS = new double[] {0.05, 0.1, 0.5 };
  private static final double[] INTERESTS = new double[] {0.015, 0.05 };
  private static final double[] DIVIDENDS = new double[] {0.005, 0.01 };

  /**
   *
   */
  @Test
  public void priceTestTrinomial() {
    final LatticeSpecification lattice = new TianLatticeSpecification();
    final double[] vols = new double[] {0.15, 0.25 };

    final int nSteps = 3585;
    final double lower = 85.;
    final double upper = 135.;
    final boolean[] tfSet = new boolean[] {true, false };
    final String type = "DoubleKnockOut";
    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 DoubleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, lower, upper,
                  DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
              double exact = price(SPOT, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double res = _modelTrinomial.getPrice(lattice, function, SPOT, vol, interest, dividend);
              assertEquals(res, exact, Math.max(exact, .1) * 1.e-1);
            }
          }
        }
      }
    }
  }

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

    final int nSteps = 3585;
    final double lower = 85.;
    final double upper = 135.;
    final boolean[] tfSet = new boolean[] {true, false };
    final String type = "DoubleKnockOut";
    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 DoubleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, lower, upper,
                  DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
              final double price = price(SPOT, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double delta = delta(SPOT, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double deltaSpotUp = delta(SPOT + eps, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double deltaSpotDown = delta(SPOT - eps, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double priceTimeUp = price(SPOT, strike, TIME + eps, vol, interest, dividend, isCall, lower, upper);
              final double priceTimeDown = price(SPOT, strike, TIME - eps, vol, interest, dividend, isCall, lower, upper);
              final double gamma = 0.5 * (deltaSpotUp - deltaSpotDown) / 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-1);
              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);
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void putCallSymmetryTest() {
    /*
     * Two sample lattices are checked
     */
    final LatticeSpecification[] lattices = new LatticeSpecification[] {new CoxRossRubinsteinLatticeSpecification(), new LeisenReimerLatticeSpecification() };
    final int steps = 401;
    final double lower = 90.;
    final double upper = 120.;
    DoubleBarrierOptionFunctionProvider.BarrierTypes out = DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut");
    for (final double strike : STRIKES) {
      final OptionFunctionProvider1D functionCall = new DoubleBarrierOptionFunctionProvider(strike, TIME, steps, true, lower, upper, out);
      final OptionFunctionProvider1D functionPut = new DoubleBarrierOptionFunctionProvider(SPOT * SPOT / strike, TIME, steps, false, SPOT * SPOT / upper, SPOT * SPOT / lower, out);
      for (final double interest : INTERESTS) {
        for (final double vol : VOLS) {
          for (final double dividend : DIVIDENDS) {
            for (final LatticeSpecification lattice : lattices) {
              final GreekResultCollection greekCall = _model.getGreeks(lattice, functionCall, SPOT, vol, interest, dividend);
              final GreekResultCollection greekPut = _model.getGreeks(lattice, functionPut, SPOT, vol, dividend, interest);
              final double priceCall = greekCall.get(Greek.FAIR_PRICE);
              final double thetaCall = greekCall.get(Greek.THETA);
              final double pricePut = greekPut.get(Greek.FAIR_PRICE);
              final double thetaPut = greekPut.get(Greek.THETA);
              final double refPrice = strike * pricePut / SPOT;
              final double refTheta = strike * thetaPut / SPOT;
              assertEquals(priceCall, refPrice, Math.max(refPrice, 0.1) * 1.e-5);
              assertEquals(thetaCall, refTheta, Math.max(Math.abs(refTheta), 0.1) * 1.e-2);
            }
          }
        }
      }
    }
  }

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

    final int nSteps = 3321;
    final double lower = 85.;
    final double upper = 135.;
    final boolean[] tfSet = new boolean[] {true, false };
    final String type = "DoubleKnockOut";
    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 DoubleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, lower, upper,
                  DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
              double exact = price(SPOT, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
              assertEquals(res, exact, Math.max(exact, .1) * 1.e-1);
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void priceOutOfRangeTest() {
    final LatticeSpecification lattice = new LeisenReimerLatticeSpecification();
    final double[] vols = new double[] {0.15, 0.25 };

    final int nSteps = 11;
    final boolean[] tfSet = new boolean[] {true, false };
    final String type = "DoubleKnockOut";
    for (final boolean isCall : tfSet) {
      final double strike = isCall ? 115. : 95.;
      final double[] lower = new double[] {85., 112., isCall ? 80. : 96. };
      final double[] upper = new double[] {101., 130., isCall ? 114. : 130. };
      for (final double interest : INTERESTS) {
        for (final double vol : vols) {
          for (final double dividend : DIVIDENDS) {
            for (int i = 0; i < lower.length; ++i) {
              final OptionFunctionProvider1D function = new DoubleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, lower[i], upper[i],
                  DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
              final double res = _model.getPrice(lattice, function, SPOT, vol, interest, dividend);
              assertEquals(res, 0.);
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @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();
    final double[] vols = new double[] {0.15, 0.25 };

    final int nSteps = 3321;
    final double lower = 85.;
    final double upper = 135.;
    final boolean[] tfSet = new boolean[] {true, false };
    final String type = "DoubleKnockOut";
    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 DoubleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, lower, upper,
                  DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
              final double price = price(SPOT, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double delta = delta(SPOT, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double deltaSpotUp = delta(SPOT + eps, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double deltaSpotDown = delta(SPOT - eps, strike, TIME, vol, interest, dividend, isCall, lower, upper);
              final double priceTimeUp = price(SPOT, strike, TIME + eps, vol, interest, dividend, isCall, lower, upper);
              final double priceTimeDown = price(SPOT, strike, TIME - eps, vol, interest, dividend, isCall, lower, upper);
              final double gamma = 0.5 * (deltaSpotUp - deltaSpotDown) / 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-1);
              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);
            }
          }
        }
      }
    }
  }

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

    final int nSteps = 21;
    final boolean[] tfSet = new boolean[] {true, false };
    final String type = "DoubleKnockOut";
    for (final boolean isCall : tfSet) {
      final double strike = isCall ? 115. : 95.;
      final double[] lower = new double[] {85., 112., isCall ? 80. : 96. };
      final double[] upper = new double[] {101., 130., isCall ? 114. : 130. };
      for (final double interest : INTERESTS) {
        for (final double vol : vols) {
          for (final double dividend : DIVIDENDS) {
            for (int i = 0; i < lower.length; ++i) {
              final OptionFunctionProvider1D function = new DoubleBarrierOptionFunctionProvider(strike, TIME, nSteps, isCall, lower[i], upper[i],
                  DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf(type));
              final double price = price(SPOT, strike, TIME, vol, interest, dividend, isCall, lower[i], upper[i]);
              final double delta = delta(SPOT, strike, TIME, vol, interest, dividend, isCall, lower[i], upper[i]);
              final double deltaSpotUp = delta(SPOT + eps, strike, TIME, vol, interest, dividend, isCall, lower[i], upper[i]);
              final double deltaSpotDown = delta(SPOT - eps, strike, TIME, vol, interest, dividend, isCall, lower[i], upper[i]);
              final double priceTimeUp = price(SPOT, strike, TIME + eps, vol, interest, dividend, isCall, lower[i], upper[i]);
              final double priceTimeDown = price(SPOT, strike, TIME - eps, vol, interest, dividend, isCall, lower[i], upper[i]);
              final double gamma = 0.5 * (deltaSpotUp - deltaSpotDown) / 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-3);
              assertEquals(res.get(Greek.DELTA), delta, Math.max(delta, 1.) * 1.e-3);
              assertEquals(res.get(Greek.GAMMA), gamma, Math.max(gamma, 1.) * 1.e-3);
              assertEquals(res.get(Greek.THETA), theta, Math.max(theta, 1.) * 1.e-3);
            }
          }
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void binomialTrinomialDiscreteDividendTest() {
    final LatticeSpecification lattice = new TianLatticeSpecification();

    final double time = 5.;
    final double[] propDividends = new double[] {0.05, 0.06, 0.05 };
    final double[] cashDividends = new double[] {3., 2., 4.5 };
    final double[] dividendTimes = new double[] {time / 6., time / 3., time / 2. };
    final DividendFunctionProvider cashDividend = new CashDividendFunctionProvider(dividendTimes, cashDividends);
    final DividendFunctionProvider propDividend = new ProportionalDividendFunctionProvider(dividendTimes, propDividends);
    final int steps = 139;
    final int stepsTri = 114;

    final boolean[] tfSet = new boolean[] {true, false };
    for (final boolean isCall : tfSet) {
      final double strike = isCall ? 115. : 95.;
      final double[] lower = new double[] {85., isCall ? 95. : 80. };
      final double[] upper = new double[] {125., isCall ? 130. : 115. };
      for (final double interest : INTERESTS) {
        for (final double vol : VOLS) {
          for (int i = 0; i < lower.length; ++i) {
            final OptionFunctionProvider1D function = new DoubleBarrierOptionFunctionProvider(strike, TIME, steps, isCall, lower[i], upper[i],
                DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
            final double priceCash = _model.getPrice(lattice, function, SPOT, vol, interest, cashDividend);
            final double priceProp = _model.getPrice(lattice, function, SPOT, vol, interest, propDividend);
            final GreekResultCollection resCash = _model.getGreeks(lattice, function, SPOT, vol, interest, cashDividend);
            final GreekResultCollection resProp = _model.getGreeks(lattice, function, SPOT, vol, interest, propDividend);
            final OptionFunctionProvider1D functionTri = new DoubleBarrierOptionFunctionProvider(strike, TIME, stepsTri, isCall, lower[i], upper[i],
                DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
            final double priceCashTrinomial = _modelTrinomial.getPrice(lattice, functionTri, SPOT, vol, interest, cashDividend);
            final double pricePropTrinomial = _modelTrinomial.getPrice(lattice, functionTri, SPOT, vol, interest, propDividend);
            final GreekResultCollection resCashTrinomial = _modelTrinomial.getGreeks(lattice, functionTri, SPOT, vol, interest, cashDividend);
            final GreekResultCollection resPropTrinomial = _modelTrinomial.getGreeks(lattice, functionTri, SPOT, vol, interest, propDividend);

            assertEquals(resCash.get(Greek.FAIR_PRICE), priceCash, 1.e-14);
            assertEquals(resCashTrinomial.get(Greek.FAIR_PRICE), priceCashTrinomial, 1.e-14);
            assertEquals(resCash.get(Greek.FAIR_PRICE), resCashTrinomial.get(Greek.FAIR_PRICE), Math.max(Math.abs(resCashTrinomial.get(Greek.FAIR_PRICE)), 1.) * 1.e-1);
            assertEquals(resCash.get(Greek.DELTA), resCashTrinomial.get(Greek.DELTA), Math.max(Math.abs(resCashTrinomial.get(Greek.DELTA)), 1.) * 1.e-1);
            assertEquals(resCash.get(Greek.GAMMA), resCashTrinomial.get(Greek.GAMMA), Math.max(Math.abs(resCashTrinomial.get(Greek.GAMMA)), 1.) * 1.e-1);
            assertEquals(resCash.get(Greek.THETA), resCashTrinomial.get(Greek.THETA), Math.max(Math.abs(resCashTrinomial.get(Greek.THETA)), 1.) * 1.);

            assertEquals(resProp.get(Greek.FAIR_PRICE), priceProp, 1.e-14);
            assertEquals(resPropTrinomial.get(Greek.FAIR_PRICE), pricePropTrinomial, 1.e-14);
            assertEquals(resProp.get(Greek.FAIR_PRICE), resPropTrinomial.get(Greek.FAIR_PRICE), Math.max(Math.abs(resPropTrinomial.get(Greek.FAIR_PRICE)), 1.) * 1.e-1);
            assertEquals(resProp.get(Greek.DELTA), resPropTrinomial.get(Greek.DELTA), Math.max(Math.abs(resPropTrinomial.get(Greek.DELTA)), 1.) * 1.e-1);
            assertEquals(resProp.get(Greek.GAMMA), resPropTrinomial.get(Greek.GAMMA), Math.max(Math.abs(resPropTrinomial.get(Greek.GAMMA)), 1.) * 1.e-1);
            assertEquals(resProp.get(Greek.THETA), resPropTrinomial.get(Greek.THETA), Math.max(Math.abs(resPropTrinomial.get(Greek.THETA)), 1.) * 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 = 701;
    final double[] vol = new double[steps];
    final double[] rate = new double[steps];
    final double[] dividend = new double[steps];
    final int stepsTri = 621;
    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) {
      final double strike = isCall ? 115. : 95.;
      final double[] lower = new double[] {85., isCall ? 95. : 80. };
      final double[] upper = new double[] {125., isCall ? 130. : 115. };
      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));

        for (int i = 0; i < lower.length; ++i) {
          final OptionFunctionProvider1D functionVanilla = new DoubleBarrierOptionFunctionProvider(strike, TIME, steps, isCall, lower[i], upper[i],
              DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
          final double resPrice = _model.getPrice(functionVanilla, SPOT, vol, rate, dividend);
          final GreekResultCollection resGreeks = _model.getGreeks(functionVanilla, SPOT, vol, rate, dividend);

          final double resPriceConst = _model.getPrice(lattice1, functionVanilla, SPOT, volRef, rateRef, dividend[0]);
          final GreekResultCollection resGreeksConst = _model.getGreeks(lattice1, functionVanilla, SPOT, volRef, rateRef, dividend[0]);
          assertEquals(resPrice, resPriceConst, Math.max(Math.abs(resPriceConst), .1) * 1.e-1);
          assertEquals(resGreeks.get(Greek.FAIR_PRICE), resGreeksConst.get(Greek.FAIR_PRICE), Math.max(Math.abs(resGreeksConst.get(Greek.FAIR_PRICE)), 0.1) * 0.1);
          assertEquals(resGreeks.get(Greek.DELTA), resGreeksConst.get(Greek.DELTA), Math.max(Math.abs(resGreeksConst.get(Greek.DELTA)), 0.1) * 0.1);
          assertEquals(resGreeks.get(Greek.GAMMA), resGreeksConst.get(Greek.GAMMA), Math.max(Math.abs(resGreeksConst.get(Greek.GAMMA)), 0.1) * 0.1);
          assertEquals(resGreeks.get(Greek.THETA), resGreeksConst.get(Greek.THETA), Math.max(Math.abs(resGreeksConst.get(Greek.THETA)), 0.1));

          final OptionFunctionProvider1D functionTri = new DoubleBarrierOptionFunctionProvider(strike, TIME, stepsTri, isCall, lower[i], upper[i],
              DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
          final double resPriceTrinomial = _modelTrinomial.getPrice(functionTri, SPOT, volTri, rateTri, dividendTri);
          assertEquals(resPriceTrinomial, resPriceConst, Math.max(Math.abs(resPriceConst), 1.) * 1.e-1);
          final GreekResultCollection resGreeksTrinomial = _modelTrinomial.getGreeks(functionTri, SPOT, volTri, rateTri, dividendTri);
          assertEquals(resGreeksTrinomial.get(Greek.FAIR_PRICE), resGreeksConst.get(Greek.FAIR_PRICE), Math.max(Math.abs(resGreeksConst.get(Greek.FAIR_PRICE)), 1.) * 0.1);
          assertEquals(resGreeksTrinomial.get(Greek.DELTA), resGreeksConst.get(Greek.DELTA), Math.max(Math.abs(resGreeksConst.get(Greek.DELTA)), 1.) * 0.1);
          assertEquals(resGreeksTrinomial.get(Greek.GAMMA), resGreeksConst.get(Greek.GAMMA), Math.max(Math.abs(resGreeksConst.get(Greek.GAMMA)), 1.) * 0.1);
          assertEquals(resGreeksTrinomial.get(Greek.THETA), resGreeksConst.get(Greek.THETA), Math.max(Math.abs(resGreeksConst.get(Greek.THETA)), 1.));
        }
      }
    }
  }

  /**
   *
   */
  @Test
  public void getLowerBarrierTest() {
    final OptionFunctionProvider1D function = new DoubleBarrierOptionFunctionProvider(100., TIME, 21, true, 88., 121.,
        DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
    assertEquals(((DoubleBarrierOptionFunctionProvider) function).getLowerBarrier(), 88.);
  }

  /**
   *
   */
  @Test
  public void getUpperBarrierTest() {
    final OptionFunctionProvider1D function = new DoubleBarrierOptionFunctionProvider(100., TIME, 21, true, 88., 121.,
        DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
    assertEquals(((DoubleBarrierOptionFunctionProvider) function).getUpperBarrier(), 121.);
  }

  /**
   *
   */
  @Test(expectedExceptions = IllegalArgumentException.class)
  public void getUnspecifiedBarrierTest() {
    final OptionFunctionProvider1D function = new DoubleBarrierOptionFunctionProvider(100., TIME, 21, true, 88., 121.,
        DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
    ((DoubleBarrierOptionFunctionProvider) function).getBarrier();
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(expectedExceptions = NotImplementedException.class)
  public void DoubleKnockInTest() {
    new DoubleBarrierOptionFunctionProvider(100., TIME, 21, true, 88., 121., DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockIn"));
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(expectedExceptions = IllegalArgumentException.class)
  public void negativeUpperBarrierTest() {
    new DoubleBarrierOptionFunctionProvider(100., TIME, 21, true, 88., -121., DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
  }

  /**
   *
   */
  @SuppressWarnings("unused")
  @Test(expectedExceptions = IllegalArgumentException.class)
  public void smallUpperBarrierTest() {
    new DoubleBarrierOptionFunctionProvider(100., TIME, 21, true, 188., 121., DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut"));
  }

  /**
   *
   */
  @Test
  public void hashCodeEqualsTest() {
    final DoubleBarrierOptionFunctionProvider.BarrierTypes type = DoubleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DoubleKnockOut");
    final AmericanSingleBarrierOptionFunctionProvider.BarrierTypes type1 = AmericanSingleBarrierOptionFunctionProvider.BarrierTypes.valueOf("DownAndOut");
    final OptionFunctionProvider1D ref = new DoubleBarrierOptionFunctionProvider(100., 1., 53, true, 90., 120., type);
    final OptionFunctionProvider1D[] function = new OptionFunctionProvider1D[] {ref, new DoubleBarrierOptionFunctionProvider(100., 1., 53, true, 90., 120., type),
        new DoubleBarrierOptionFunctionProvider(100., 1., 53, true, 90., 121., type), new AmericanSingleBarrierOptionFunctionProvider(100., 1., 53, true, 90., type1),
        new AmericanVanillaOptionFunctionProvider(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 div, final boolean isCall,
      final double lower, final double upper) {
    final int rg = 4;
    final double sigmaRootT = vol * Math.sqrt(time);
    final double second = (interest - div + 0.5 * vol * vol) * time;
    final double mu = 2. * (interest - div) / vol / vol + 1.;

    if (spot <= lower || spot >= upper) {
      return 0.;
    }

    double tmp1 = 0.;
    double tmp2 = 0.;
    if (isCall) {
      for (int i = -rg; i < rg + 1; ++i) {
        final double d1 = (Math.log(spot * Math.pow(upper, 2. * i) / strike / Math.pow(lower, 2. * i)) + second) / sigmaRootT;
        final double d2 = (Math.log(spot * Math.pow(upper, 2. * i - 1) / Math.pow(lower, 2. * i)) + second) / sigmaRootT;
        final double d3 = (Math.log(Math.pow(lower, 2. * i + 2) / spot / strike / Math.pow(upper, 2. * i)) + second) / sigmaRootT;
        final double d4 = (Math.log(Math.pow(lower, 2. * i + 2.) / spot / Math.pow(upper, 2. * i + 1.)) + second) / sigmaRootT;

        tmp1 += Math.pow(upper / lower, i * mu) * (NORMAL.getCDF(d1) - NORMAL.getCDF(d2)) - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) * (NORMAL.getCDF(d3) - NORMAL.getCDF(d4));
        tmp2 += Math.pow(upper / lower, i * mu - i * 2.) * (NORMAL.getCDF(d1 - sigmaRootT) - NORMAL.getCDF(d2 - sigmaRootT)) - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu - 2.) *
            (NORMAL.getCDF(d3 - sigmaRootT) - NORMAL.getCDF(d4 - sigmaRootT));
      }
      final double res = tmp1 * spot * Math.exp(-div * time) - tmp2 * strike * Math.exp(-interest * time);
      return res <= 0. ? 0. : res;
    }
    for (int i = -rg; i < rg + 1; ++i) {
      final double y1 = (Math.log(spot * Math.pow(upper, 2. * i) / Math.pow(lower, 2. * i + 1.)) + second) / sigmaRootT;
      final double y2 = (Math.log(spot * Math.pow(upper, 2. * i) / strike / Math.pow(lower, 2. * i)) + second) / sigmaRootT;
      final double y3 = (Math.log(Math.pow(lower, 2. * i + 1.) / spot / Math.pow(upper, 2. * i)) + second) / sigmaRootT;
      final double y4 = (Math.log(Math.pow(lower, 2. * i + 2.) / spot / strike / Math.pow(upper, 2. * i)) + second) / sigmaRootT;

      tmp2 += Math.pow(upper / lower, i * mu - i * 2.) * (NORMAL.getCDF(y1 - sigmaRootT) - NORMAL.getCDF(y2 - sigmaRootT)) - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu - 2.) *
          (NORMAL.getCDF(y3 - sigmaRootT) - NORMAL.getCDF(y4 - sigmaRootT));
      tmp1 += Math.pow(upper / lower, i * mu) * (NORMAL.getCDF(y1) - NORMAL.getCDF(y2)) - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) * (NORMAL.getCDF(y3) - NORMAL.getCDF(y4));
    }
    final double res = tmp2 * strike * Math.exp(-interest * time) - tmp1 * spot * Math.exp(-div * time);
    return res <= 0. ? 0. : res;
  }

  private double delta(final double spot, final double strike, final double time, final double vol, final double interest, final double div, final boolean isCall,
      final double lower, final double upper) {
    final int rg = 4;
    final double sigmaRootT = vol * Math.sqrt(time);
    final double second = (interest - div + 0.5 * vol * vol) * time;
    final double mu = 2. * (interest - div) / vol / vol + 1.;

    if (spot < lower || spot > upper) {
      return 0.;
    }

    double tmp1 = 0.;
    double tmp1Diff = 0.;
    double tmp2Diff = 0.;
    if (isCall) {
      final double d12Diff = 1. / spot / sigmaRootT;
      final double d34Diff = -d12Diff;
      for (int i = -rg; i < rg + 1; ++i) {
        final double d1 = (Math.log(spot * Math.pow(upper, 2. * i) / strike / Math.pow(lower, 2. * i)) + second) / sigmaRootT;
        final double d2 = (Math.log(spot * Math.pow(upper, 2. * i - 1) / Math.pow(lower, 2. * i)) + second) / sigmaRootT;
        final double d3 = (Math.log(Math.pow(lower, 2. * i + 2) / spot / strike / Math.pow(upper, 2. * i)) + second) / sigmaRootT;
        final double d4 = (Math.log(Math.pow(lower, 2. * i + 2.) / spot / Math.pow(upper, 2. * i + 1.)) + second) / sigmaRootT;

        tmp1 += Math.pow(upper / lower, i * mu) * (NORMAL.getCDF(d1) - NORMAL.getCDF(d2)) - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) * (NORMAL.getCDF(d3) - NORMAL.getCDF(d4));
        tmp1Diff += Math.pow(upper / lower, i * mu) * (NORMAL.getPDF(d1) - NORMAL.getPDF(d2)) * d12Diff - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) *
            (NORMAL.getPDF(d3) - NORMAL.getPDF(d4)) * d34Diff + mu * Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) / spot * (NORMAL.getCDF(d3) - NORMAL.getCDF(d4));
        tmp2Diff += Math.pow(upper / lower, i * mu - i * 2.) * (NORMAL.getPDF(d1 - sigmaRootT) - NORMAL.getPDF(d2 - sigmaRootT)) * d12Diff -
            Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu - 2.) *
            (NORMAL.getPDF(d3 - sigmaRootT) - NORMAL.getPDF(d4 - sigmaRootT)) * d34Diff + (mu - 2.) / spot * Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu - 2.) *
            (NORMAL.getCDF(d3 - sigmaRootT) - NORMAL.getCDF(d4 - sigmaRootT));
      }
      final double res = tmp1 * Math.exp(-div * time) + tmp1Diff * spot * Math.exp(-div * time) - tmp2Diff * strike * Math.exp(-interest * time);
      return res;
    }
    final double y12Diff = 1. / spot / sigmaRootT;
    final double y34Diff = -y12Diff;
    for (int i = -rg; i < rg + 1; ++i) {
      final double y1 = (Math.log(spot * Math.pow(upper, 2. * i) / Math.pow(lower, 2. * i + 1.)) + second) / sigmaRootT;
      final double y2 = (Math.log(spot * Math.pow(upper, 2. * i) / strike / Math.pow(lower, 2. * i)) + second) / sigmaRootT;
      final double y3 = (Math.log(Math.pow(lower, 2. * i + 1.) / spot / Math.pow(upper, 2. * i)) + second) / sigmaRootT;
      final double y4 = (Math.log(Math.pow(lower, 2. * i + 2.) / spot / strike / Math.pow(upper, 2. * i)) + second) / sigmaRootT;

      tmp2Diff += Math.pow(upper / lower, i * mu - i * 2.) * (NORMAL.getPDF(y1 - sigmaRootT) - NORMAL.getPDF(y2 - sigmaRootT)) * y12Diff -
          Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu - 2.) *
          (NORMAL.getPDF(y3 - sigmaRootT) - NORMAL.getPDF(y4 - sigmaRootT)) * y34Diff + (mu - 2.) / spot * Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu - 2.) *
          (NORMAL.getCDF(y3 - sigmaRootT) - NORMAL.getCDF(y4 - sigmaRootT));
      tmp1Diff += Math.pow(upper / lower, i * mu) * (NORMAL.getPDF(y1) - NORMAL.getPDF(y2)) * y12Diff - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) *
          (NORMAL.getPDF(y3) - NORMAL.getPDF(y4)) * y34Diff
          + mu / spot * Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) * (NORMAL.getCDF(y3) - NORMAL.getCDF(y4));
      tmp1 += Math.pow(upper / lower, i * mu) * (NORMAL.getCDF(y1) - NORMAL.getCDF(y2)) - Math.pow(Math.pow(lower, i + 1.) / Math.pow(upper, i) / spot, mu) * (NORMAL.getCDF(y3) - NORMAL.getCDF(y4));
    }
    final double res = tmp2Diff * strike * Math.exp(-interest * time) - tmp1Diff * spot * Math.exp(-div * time) - tmp1 * Math.exp(-div * time);
    return res;
  }

  /**
   * test for analytic formula
   */
  @Test(enabled = false)
  public void functionTest() {
    final boolean[] tfSet = new boolean[] {true, false };
    final double eps = 1.e-6;
    final double lower = 85.;
    final double upper = 135.;
    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 double delta = delta(SPOT, strike, TIME, vol, interest, interest - dividend, isCall, lower, upper);
              final double upSpot = price(SPOT + eps, strike, TIME, vol, interest, interest - dividend, isCall, lower, upper);
              final double downSpot = price(SPOT - eps, strike, TIME, vol, interest, interest - dividend, isCall, lower, upper);
              assertEquals(delta, 0.5 * (upSpot - downSpot) / eps, eps);
            }
          }
        }
      }
    }
  }

  //  @Test
  //  public void test() {
  //    final double spot = 100.;
  //    final double strike = 100.;
  //    final double interest = 0.1;
  //    final double div = 0.;
  //
  //    final double[] vol = new double[] {0.15, 0.25, 0.35 };
  //    final double[] time = new double[] {0.25, 0.5 };
  //    final double[] upper = new double[] {150., 140., 130., 120., 110. };
  //    final double[] lower = new double[] {50., 60., 70., 80., 90. };
  //
  //    for (int i = 0; i < upper.length; ++i) {
  //      for (int j = 0; j < time.length; ++j) {
  //        for (int k = 0; k < vol.length; ++k) {
  //          final double callPrice = price(spot, strike, time[j], vol[k], interest, div, true, upper[i], lower[i]);
  //          System.out.print(callPrice + "\t");
  //          final double dual = price(strike, spot, time[j], vol[k], div, interest, false, spot * strike / lower[i], spot * strike / upper[i]);
  //          assertEquals(callPrice, dual, Math.max(callPrice, 1.) * 1.e-10);
  //
  //        }
  //      }
  //      System.out.print("\n");
  //    }
  //
  //  }
}
TOP

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

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.