com.opengamma.strata.pricer.impl.tree.TrinomialTree Maven / Gradle / Ivy
/*
* 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 com.opengamma.strata.basics.value.ValueDerivatives;
import com.opengamma.strata.collect.ArgChecker;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.pricer.fxopt.RecombiningTrinomialTreeData;
/**
* Trinomial tree.
*
* Option pricing model based on trinomial tree. Trinomial lattice is defined by {@code LatticeSpecification}
* and the option to price is specified by {@code OptionFunction}.
*
* Option pricing with non-uniform tree is realised by specifying {@code RecombiningTrinomialTreeData}.
*/
public class TrinomialTree {
/**
* Price an option under the specified trinomial lattice.
*
* It is assumed that the volatility, interest rate and continuous dividend rate are constant
* over the lifetime of the option.
*
* @param function the option
* @param lattice the lattice specification
* @param spot the spot
* @param volatility the volatility
* @param interestRate the interest rate
* @param dividendRate the dividend rate
* @return the option price
*/
public double optionPrice(
OptionFunction function,
LatticeSpecification lattice,
double spot,
double volatility,
double interestRate,
double dividendRate) {
int nSteps = function.getNumberOfSteps();
double timeToExpiry = function.getTimeToExpiry();
double dt = timeToExpiry / (double) nSteps;
double discount = Math.exp(-interestRate * dt);
DoubleArray params = lattice.getParametersTrinomial(volatility, interestRate - dividendRate, dt);
double middleFactor = params.get(1);
double downFactor = params.get(2);
double upProbability = params.get(3);
double midProbability = params.get(4);
double downProbability = params.get(5);
ArgChecker.isTrue(upProbability > 0d, "upProbability should be greater than 0");
ArgChecker.isTrue(upProbability < 1d, "upProbability should be smaller than 1");
ArgChecker.isTrue(midProbability > 0d, "midProbability should be greater than 0");
ArgChecker.isTrue(midProbability < 1d, "midProbability should be smaller than 1");
ArgChecker.isTrue(downProbability > 0d, "downProbability should be greater than 0");
DoubleArray values = function.getPayoffAtExpiryTrinomial(spot, downFactor, middleFactor);
for (int i = nSteps - 1; i > -1; --i) {
values = function.getNextOptionValues(discount, upProbability, midProbability, downProbability, values, spot,
downFactor, middleFactor, i);
}
return values.get(0);
}
/**
* Price an option under the specified trinomial tree gird.
*
* @param function the option
* @param data the trinomial tree data
* @return the option price
*/
public double optionPrice(
OptionFunction function,
RecombiningTrinomialTreeData data) {
int nSteps = data.getNumberOfSteps();
ArgChecker.isTrue(nSteps == function.getNumberOfSteps(), "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);
}
/**
* Compute option price and delta under the specified trinomial tree gird.
*
* The delta is the first derivative of the price with respect to spot, and approximated by the data embedded in
* the trinomial tree.
*
* @param function the option
* @param data the trinomial tree data
* @return the option price and spot delta
*/
public ValueDerivatives optionPriceAdjoint(
OptionFunction function,
RecombiningTrinomialTreeData data) {
int nSteps = data.getNumberOfSteps();
ArgChecker.isTrue(nSteps == function.getNumberOfSteps(), "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));
}
}