public virtual void test_formula() { CoxRossRubinsteinLatticeSpecification test = new CoxRossRubinsteinLatticeSpecification(); DoubleArray computed = test.getParametersTrinomial(VOL, RATE, DT); double u = Math.Exp(VOL * Math.Sqrt(2.0 * DT)); double d = Math.Exp(-VOL * Math.Sqrt(2.0 * DT)); double up = Math.Pow((Math.Exp(0.5 * RATE * DT) - Math.Exp(-VOL * Math.Sqrt(0.5 * DT))) / (Math.Exp(VOL * Math.Sqrt(0.5 * DT)) - Math.Exp(-VOL * Math.Sqrt(0.5 * DT))), 2); double dp = Math.Pow((Math.Exp(VOL * Math.Sqrt(0.5 * DT)) - Math.Exp(0.5 * RATE * DT)) / (Math.Exp(VOL * Math.Sqrt(0.5 * DT)) - Math.Exp(-VOL * Math.Sqrt(0.5 * DT))), 2); DoubleArray expected = DoubleArray.of(u, 1d, d, up, 1d - up - dp, dp); assertTrue(DoubleArrayMath.fuzzyEquals(computed.toArray(), expected.toArray(), 1.0e-14)); }
/// <summary> /// Test consistency between price methods, and Greek via finite difference. /// </summary> public virtual void test_trinomialTree() { int nSteps = 135; double dt = TIME / nSteps; LatticeSpecification lattice = new CoxRossRubinsteinLatticeSpecification(); double fdEps = 1.0e-4; foreach (bool isCall in new bool[] { true, false }) { foreach (double strike in STRIKES) { foreach (double interest in INTERESTS) { foreach (double vol in VOLS) { foreach (double dividend in 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 }; IList <DoubleMatrix> prob = new List <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.Value, 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); } } } } } }