public virtual void test_trinomialTree() { int nSteps = 135; LatticeSpecification[] lattices = new LatticeSpecification[] { new CoxRossRubinsteinLatticeSpecification(), new TrigeorgisLatticeSpecification() }; double tol = 5.0e-3; 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 exact = BlackScholesFormulaRepository.price(SPOT, strike, TIME, vol, interest, interest - dividend, isCall); foreach (LatticeSpecification lattice in lattices) { double computed = TRINOMIAL_TREE.optionPrice(function, lattice, SPOT, vol, interest, dividend); assertEquals(computed, exact, Math.Max(exact, 1d) * tol); } } } } } } }
//----------------------------------------------------------------------- private Pair <ImmutableList <double[]>, RecombiningTrinomialTreeData> calibrate(System.Func <DoublesPair, double> impliedVolatilitySurface, double spot, System.Func <double, double> interestRate, System.Func <double, double> dividendRate) { double[][] stateValue = new double[nSteps + 1][]; double[] df = new double[nSteps]; double[] timePrim = new double[nSteps + 1]; IList <DoubleMatrix> probability = new List <DoubleMatrix>(nSteps); int nTotal = (nSteps - 1) * (nSteps - 1) + 1; double[] timeRes = new double[nTotal]; double[] spotRes = new double[nTotal]; double[] volRes = new double[nTotal]; // uniform grid based on TrigeorgisLatticeSpecification double volatility = impliedVolatilitySurface(DoublesPair.of(maxTime, spot)); double dt = maxTime / nSteps; double dx = volatility * Math.Sqrt(3d * dt); double upFactor = Math.Exp(dx); double downFactor = Math.Exp(-dx); double[] adSec = new double[2 * nSteps + 1]; double[] assetPrice = new double[2 * nSteps + 1]; for (int i = nSteps; i > -1; --i) { timePrim[i] = dt * i; if (i == 0) { resolveFirstLayer(interestRate, dividendRate, nTotal, dt, spot, adSec, assetPrice, timeRes, spotRes, volRes, df, stateValue, probability); } else { double zeroRate = interestRate(timePrim[i]); double zeroDividendRate = dividendRate(timePrim[i]); double zeroCostRate = zeroRate - zeroDividendRate; int nNodes = 2 * i + 1; double[] assetPriceLocal = new double[nNodes]; double[] callOptionPrice = new double[nNodes]; double[] putOptionPrice = new double[nNodes]; int position = i - 1; double assetTmp = spot * Math.Pow(upFactor, i); // call options for upper half nodes for (int j = nNodes - 1; j > position - 1; --j) { assetPriceLocal[j] = assetTmp; double impliedVol = impliedVolatilitySurface(DoublesPair.of(timePrim[i], assetPriceLocal[j])); callOptionPrice[j] = BlackScholesFormulaRepository.price(spot, assetPriceLocal[j], timePrim[i], impliedVol, zeroRate, zeroCostRate, true); assetTmp *= downFactor; } // put options for lower half nodes assetTmp = spot * Math.Pow(downFactor, i); for (int j = 0; j < position + 2; ++j) { assetPriceLocal[j] = assetTmp; double impliedVol = impliedVolatilitySurface(DoublesPair.of(timePrim[i], assetPriceLocal[j])); putOptionPrice[j] = BlackScholesFormulaRepository.price(spot, assetPriceLocal[j], timePrim[i], impliedVol, zeroRate, zeroCostRate, false); assetTmp *= upFactor; } resolveLayer(interestRate, dividendRate, i, nTotal, position, dt, zeroRate, zeroDividendRate, callOptionPrice, putOptionPrice, adSec, assetPrice, assetPriceLocal, timeRes, spotRes, volRes, df, stateValue, probability); } } ImmutableList <double[]> localVolData = ImmutableList.of(timeRes, spotRes, volRes); RecombiningTrinomialTreeData treeData = RecombiningTrinomialTreeData.of(DoubleMatrix.ofUnsafe(stateValue), probability, DoubleArray.ofUnsafe(df), DoubleArray.ofUnsafe(timePrim)); return(Pair.of(localVolData, treeData)); }