/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.tree; import static org.testng.Assert.assertEquals; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.testng.annotations.Test; import com.opengamma.strata.basics.value.ValueDerivatives; import com.opengamma.strata.collect.array.DoubleArray; import com.opengamma.strata.collect.array.DoubleMatrix; import com.opengamma.strata.pricer.fxopt.RecombiningTrinomialTreeData; import com.opengamma.strata.product.common.PutCall; /** * Test {@link TrinomialTree}. * <p> * Further tests are done for implementations of {@code OptionFunction}. See their test classes. */ @Test public class TrinomialTreeTest { private static final TrinomialTree TRINOMIAL_TREE = new TrinomialTree(); private static final double SPOT = 105.; private static final double[] STRIKES = new double[] {81., 97., 105., 105.1, 114., 128. }; private static final double TIME = 1.25; private static final double[] INTERESTS = new double[] {-0.01, 0.0, 0.05 }; private static final double[] VOLS = new double[] {0.05, 0.1, 0.5 }; private static final double[] DIVIDENDS = new double[] {0.0, 0.02 }; /** * Test consistency between price methods, and Greek via finite difference. */ public void test_trinomialTree() { int nSteps = 135; double dt = TIME / nSteps; LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification(); double fdEps = 1.0e-4; for (boolean isCall : new boolean[] {true, false }) { for (double strike : STRIKES) { for (double interest : INTERESTS) { for (double vol : VOLS) { for (double dividend : DIVIDENDS) { OptionFunction function = EuropeanVanillaOptionFunction.of(strike, TIME, PutCall.ofPut(!isCall), nSteps); double[] params = lattice.getParametersTrinomial(vol, interest - dividend, dt).toArray(); DoubleArray time = DoubleArray.of(nSteps + 1, i -> dt * i); DoubleArray df = DoubleArray.of(nSteps, i -> Math.exp(-interest * dt)); double[][] stateValue = new double[nSteps + 1][]; stateValue[0] = new double[] {SPOT }; List<DoubleMatrix> prob = new ArrayList<DoubleMatrix>(); double[] probs = new double[] {params[5], params[4], params[3] }; for (int i = 0; i < nSteps; ++i) { int index = i; stateValue[i + 1] = DoubleArray.of(2 * i + 3, j -> SPOT * Math.pow(params[2], index + 1 - j) * Math.pow(params[1], j)).toArray(); double[][] probMatrix = new double[2 * i + 1][]; Arrays.fill(probMatrix, probs); prob.add(DoubleMatrix.ofUnsafe(probMatrix)); } RecombiningTrinomialTreeData treeData = RecombiningTrinomialTreeData.of(DoubleMatrix.ofUnsafe(stateValue), prob, df, time); double priceData = TRINOMIAL_TREE.optionPrice(function, treeData); double priceParams = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend); assertEquals(priceData, priceParams); ValueDerivatives priceDeriv = TRINOMIAL_TREE.optionPriceAdjoint(function, treeData); assertEquals(priceDeriv.getValue(), priceData); double priceUp = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT + fdEps, vol, interest, dividend); double priceDw = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT - fdEps, vol, interest, dividend); double fdDelta = 0.5 * (priceUp - priceDw) / fdEps; assertEquals(priceDeriv.getDerivative(0), fdDelta, 3.0e-2); } } } } } } }