/// <summary>
        /// Price an option under the specified trinomial tree gird.
        /// </summary>
        /// <param name="function">  the option </param>
        /// <param name="data">  the trinomial tree data </param>
        /// <returns> the option price </returns>
        public virtual double optionPrice(OptionFunction function, RecombiningTrinomialTreeData data)
        {
            int nSteps = data.NumberOfSteps;

            ArgChecker.isTrue(nSteps == function.NumberOfSteps, "mismatch in number of steps");
            DoubleArray values = function.getPayoffAtExpiryTrinomial(data.getStateValueAtLayer(nSteps));

            for (int i = nSteps - 1; i > -1; --i)
            {
                values = function.getNextOptionValues(data.getDiscountFactorAtLayer(i), data.getProbabilityAtLayer(i), data.getStateValueAtLayer(i), values, i);
            }
            return(values.get(0));
        }
示例#2
0
        /// <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);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Compute option price and delta under the specified trinomial tree gird.
        /// <para>
        /// The delta is the first derivative of the price with respect to spot, and approximated by the data embedded in
        /// the trinomial tree.
        ///
        /// </para>
        /// </summary>
        /// <param name="function">  the option </param>
        /// <param name="data">  the trinomial tree data </param>
        /// <returns> the option price and spot delta </returns>
        public virtual ValueDerivatives optionPriceAdjoint(OptionFunction function, RecombiningTrinomialTreeData data)
        {
            int nSteps = data.NumberOfSteps;

            ArgChecker.isTrue(nSteps == function.NumberOfSteps, "mismatch in number of steps");
            DoubleArray values = function.getPayoffAtExpiryTrinomial(data.getStateValueAtLayer(nSteps));
            double      delta  = 0d;

            for (int i = nSteps - 1; i > -1; --i)
            {
                values = function.getNextOptionValues(data.getDiscountFactorAtLayer(i), data.getProbabilityAtLayer(i), data.getStateValueAtLayer(i), values, i);
                if (i == 1)
                {
                    DoubleArray stateValue = data.getStateValueAtLayer(1);
                    double      d1         = (values.get(2) - values.get(1)) / (stateValue.get(2) - stateValue.get(1));
                    double      d2         = (values.get(1) - values.get(0)) / (stateValue.get(1) - stateValue.get(0));
                    delta = 0.5 * (d1 + d2);
                }
            }
            return(ValueDerivatives.of(values.get(0), DoubleArray.of(delta)));
        }
示例#4
0
        //-----------------------------------------------------------------------
        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));
        }