AsianOption.java

package net.finmath.tree.assetderivativevaluation.products;

/**
 * Fixed-strike Asian option using FULL non-recombining tree expansion.
 *
 * State variables:
 * <ul>
 * 	<li>state[0] = running sum of fixing spots</li>
 * 	<li>state[1] = fixing count</li>
 * </ul>
 *
 * Payoff at maturity:
 * <ul>
 * 	<li>max((sum / count) - K, 0)</li>
 * </ul>
 *
 * The averaging can be performed on any subset of the time grid by specifying fixingTimeIndices.
 *
 * No path compression: number of nodes at level j is B^j.
 */
public class AsianOption extends AbstractPathDependentProduct {

	private final double strike;

	/**
	 * @param maturity Maturity in model time units.
	 * @param strike Strike K.
	 * @param fixingTimeIndices Subset of model time indices included in the average.
	 */
	public AsianOption(
			final double maturity,
			final double strike,
			final int[] fixingTimeIndices) {
		super(maturity, fixingTimeIndices);
		this.strike = strike;
	}

	@Override
	protected int getNumberOfStateVariables() {
		return 2; // sum, count
	}

	@Override
	protected void initializeState(
			final double spotAtNode,
			final int timeIndex,
			final boolean isFixing,
			final double[] stateOut) {

		stateOut[0] = isFixing ? spotAtNode : 0.0; // sum
		stateOut[1] = isFixing ? 1.0 : 0.0;        // count stored as double for simplicity
	}

	@Override
	protected void evolveState(
			final double[] parentState,
			final double spotAtChild,
			final int timeIndexChild,
			final boolean isFixingChild,
			final double[] childStateOut) {

		childStateOut[0] = parentState[0] + (isFixingChild ? spotAtChild : 0.0);
		childStateOut[1] = parentState[1] + (isFixingChild ? 1.0 : 0.0);
	}

	@Override
	protected double payoff(final double[] terminalState, final double spotAtMaturity) {
		final double sum = terminalState[0];
		final double cnt = terminalState[1];

		final double avg = (cnt > 0.0) ? (sum / cnt) : 0.0;

		return Math.max(avg - strike, 0.0);
	}
}