All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.finmath.timeseries.models.parametric.DisplacedLognormalARMAGARCH Maven / Gradle / Ivy

Go to download

finmath lib is a Mathematical Finance Library in Java. It provides algorithms and methodologies related to mathematical finance.

There is a newer version: 6.0.19
Show newest version
/*
 * (c) Copyright Christian P. Fries, Germany. All rights reserved. Contact: [email protected].
 * 
 * Created on 15.07.2012
 */

package net.finmath.timeseries.models.parametric;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import net.finmath.timeseries.HistoricalSimulationModel;

import org.apache.commons.math3.analysis.MultivariateFunction;
import org.apache.commons.math3.optimization.GoalType;
import org.apache.commons.math3.optimization.PointValuePair;

/**
 * Displaced lognormal process with ARAMAGARCH(1,1) volatility.
 * 
 * @author Christian Fries
 */
public class DisplacedLognormalARMAGARCH implements HistoricalSimulationModel {

	private double[] values;	
	private double lowerBoundDisplacement;
	private double upperBoundDisplacement = 10000000;
	private int windowIndexStart;
	private int windowIndexEnd;
	private int maxIterations = 1000000;

	public DisplacedLognormalARMAGARCH(double[] values) {
		this.values = values;
		this.windowIndexStart	= 0;
		this.windowIndexEnd		= values.length-1;

		double valuesMin = Double.MAX_VALUE;
		for (int i = windowIndexStart; i <= windowIndexEnd; i++) {
			valuesMin = Math.min(values[i], valuesMin);
		}
		this.lowerBoundDisplacement = -valuesMin+1;

	}

	public DisplacedLognormalARMAGARCH(double[] values, double lowerBoundDisplacement) {
		this.values = values;
		this.windowIndexStart	= 0;
		this.windowIndexEnd		= values.length-1;

		double valuesMin = Double.MAX_VALUE;
		for (int i = windowIndexStart; i <= windowIndexEnd; i++) {
			valuesMin = Math.min(values[i], valuesMin);
		}
		this.lowerBoundDisplacement = Math.max(-valuesMin+1,lowerBoundDisplacement);

	}

	public DisplacedLognormalARMAGARCH(double[] values, int windowIndexStart, int windowIndexEnd) {
		this.values = values;
		this.windowIndexStart	= windowIndexStart;
		this.windowIndexEnd		= windowIndexEnd;

		double valuesMin = Double.MAX_VALUE;
		for (int i = windowIndexStart; i <= windowIndexEnd; i++) {
			valuesMin = Math.min(values[i], valuesMin);
		}
		this.lowerBoundDisplacement = -valuesMin+1;
	}

	public DisplacedLognormalARMAGARCH(double[] values, double lowerBoundDisplacement, int windowIndexStart, int windowIndexEnd) {
		this.values = values;
		this.windowIndexStart	= windowIndexStart;
		this.windowIndexEnd		= windowIndexEnd;

		double valuesMin = Double.MAX_VALUE;
		for (int i = windowIndexStart; i <= windowIndexEnd; i++) {
			valuesMin = Math.min(values[i], valuesMin);
		}
		this.lowerBoundDisplacement = Math.max(-valuesMin+1,lowerBoundDisplacement);
	}

	/* (non-Javadoc)
	 * @see net.finmath.timeseries.HistoricalSimulationModel#getCloneWithWindow(int, int)
	 */
	@Override
	public HistoricalSimulationModel getCloneWithWindow(int windowIndexStart, int windowIndexEnd) {
		return new DisplacedLognormalARMAGARCH(this.values, windowIndexStart, windowIndexEnd);
	}

	public HistoricalSimulationModel getCloneWithWindow(double lowerBoundDisplacement, int windowIndexStart, int windowIndexEnd) {
		return new DisplacedLognormalARMAGARCH(this.values, lowerBoundDisplacement, windowIndexStart, windowIndexEnd);
	}

	public double getLogLikelihoodForParameters(double theta, double mu, double omega, double alpha, double beta, double displacement)
	{
		double logLikelihood = 0.0;

		double volScaling	= (1+Math.abs(displacement));
		double evalPrev		= 0.0;
		double eval			= volScaling * (Math.log((values[windowIndexStart+1]+displacement)/(values[windowIndexStart+1-1]+displacement)));
		double h			= omega / (1.0 - alpha - beta);
		double m			= 0.0; // xxx how to init?
		for (int i = windowIndexStart+1; i <= windowIndexEnd-1; i++) {
			//double eval			= volScaling * (Math.log((values[i]+displacement)/(values[i-1]+displacement)));
			m = -theta * m + eval - mu * evalPrev;
			h = (omega + alpha * m * m) + beta * h;

			double evalNext	= volScaling * (Math.log((values[i+1]+displacement)/(values[i]+displacement)));
			double mNext =  -theta * m + evalNext - mu * eval;
			logLikelihood += - Math.log(h) - 2 * Math.log((values[i+1]+displacement)/volScaling) -  mNext* mNext / h;

			evalPrev = eval;
			eval = evalNext;
		}
		logLikelihood += - Math.log(2 * Math.PI) * (double)(windowIndexEnd-windowIndexStart);
		logLikelihood *= 0.5;

		return logLikelihood;
	}

	public double getLastResidualForParameters(double mu, double theta, double omega, double alpha, double beta, double displacement) {
		double volScaling = (1+Math.abs(displacement));
		double h = omega / (1.0 - alpha - beta);
		for (int i = windowIndexStart+1; i <= windowIndexEnd; i++) {
			double eval	= volScaling * (Math.log((values[i]+displacement)/(values[i-1]+displacement)));
			//			double eval	= volScaling * (values[i]-values[i-1])/(values[i-1]+displacement);
			h = omega + alpha * eval * eval + beta * h;
		}

		return h;
	}

	public double[] getQuantilPredictionsForParameters(double theta, double mu, double omega, double alpha, double beta, double displacement, double[] quantiles) {
		double[] szenarios = new double[windowIndexEnd-windowIndexStart+1-1];

		double volScaling = (1+Math.abs(displacement));
		double h = omega / (1.0 - alpha - beta);
		double vol = Math.sqrt(h) / volScaling;
		double evalPrev		= 0.0;
		double m			= 0.0;
		for (int i = windowIndexStart+1; i <= windowIndexEnd; i++) {
			double y = Math.log((values[i]+displacement)/(values[i-1]+displacement));
			//			double y = (values[i]-values[i-1])/(values[i-1]+displacement);

			double eval		= volScaling * y;
			m = eval - theta * m - mu * evalPrev;

			szenarios[i-windowIndexStart-1]	= m / vol / volScaling;

			h = (omega + alpha * m * m) + beta * h;
			vol = Math.sqrt(h) / volScaling;
			evalPrev = eval;
		}
		java.util.Arrays.sort(szenarios);

		double[] quantileValues = new double[quantiles.length];
		for(int i=0; i getBestParameters() {
		return getBestParameters(null);
	}

	/* (non-Javadoc)
	 * @see net.finmath.timeseries.HistoricalSimulationModel#getBestParameters(java.util.Map)
	 */
	@Override
	public Map getBestParameters(Map guess) {

		// Create the objective function for the solver
		class GARCHMaxLikelihoodFunction implements MultivariateFunction, Serializable {

			private static final long serialVersionUID = 7072187082052755854L;

			public double value(double[] variables) {
				/*
				 * Transform variables: The solver variables are in (-\infty, \infty).
				 * We transform the variable to the admissible domain for GARCH, that is
				 * omega > 0, 0 < alpha < 1, 0 < beta < (1-alpha), displacement > lowerBoundDisplacement
				 */
				double omega	= Math.exp(variables[0]);
				double mucorr	= Math.exp(-Math.exp(-variables[1]));
				double muema	= Math.exp(-Math.exp(-variables[2]));
				double beta		= mucorr * muema;
				double alpha	= mucorr - beta;
//				double alpha = 1.0/(1.0+Math.exp(-variables[1]));
//				double beta = (1.0-alpha)*1.0/(1.0+Math.exp(-variables[2]));
				double displacementNormed = 1.0/(1.0+Math.exp(-variables[3]));
				double displacement = (upperBoundDisplacement-lowerBoundDisplacement)*displacementNormed+lowerBoundDisplacement;
				double theta	= variables[4];
				double mu		= variables[5];
				double logLikelihood = getLogLikelihoodForParameters(theta, mu, omega,alpha,beta,displacement);

				// Penalty to prevent solver from hitting the bounds
				logLikelihood -= Math.max(1E-30-omega,0)/1E-30;
				logLikelihood -= Math.max(1E-30-alpha,0)/1E-30;
				logLikelihood -= Math.max((alpha-1)+1E-30,0)/1E-30;
				logLikelihood -= Math.max(1E-30-beta,0)/1E-30;
				logLikelihood -= Math.max((beta-1)+1E-30,0)/1E-30;
				logLikelihood -= Math.max(1E-30-displacementNormed,0)/1E-30;
				logLikelihood -= Math.max((displacementNormed-1)+1E-30,0)/1E-30;

				return logLikelihood;
			}

		}
		GARCHMaxLikelihoodFunction objectiveFunction = new GARCHMaxLikelihoodFunction();

		// Create a guess for the solver
		double guessOmega = 1.0;
		double guessAlpha = 0.2;
		double guessBeta = 0.2;
		double guessDisplacement = (lowerBoundDisplacement + upperBoundDisplacement) / 2.0;
		double guessTheta = 0.0;
		double guessMu = 0.0;	
		if(guess != null) {
			// A guess was provided, use that one
			guessOmega			= guess.get("Omega");
			guessAlpha			= guess.get("Alpha");
			guessBeta			= guess.get("Beta");
			guessDisplacement	= guess.get("Displacement");
			guessTheta			= guess.get("Theta");
			guessMu				= guess.get("Mu");
		}

		// Constrain guess to admissible range
		guessOmega			= restrictToOpenSet(guessOmega, 0.0, Double.MAX_VALUE);
		guessAlpha			= restrictToOpenSet(guessAlpha, 0.0, 1.0);
		guessBeta			= restrictToOpenSet(guessBeta, 0.0, 1.0-guessAlpha);
		guessDisplacement	= restrictToOpenSet(guessDisplacement, lowerBoundDisplacement, upperBoundDisplacement);

		double guessMucorr	= guessAlpha + guessBeta;
		double guessMuema	= guessBeta / (guessAlpha+guessBeta);

		// Transform guess to solver coordinates
		double[] guessParameters = new double[6];
		guessParameters[0] = Math.log(guessOmega);
		guessParameters[1] = -Math.log(-Math.log(guessMucorr));
		guessParameters[2] = -Math.log(-Math.log(guessMuema));
		guessParameters[3] = -Math.log(1.0/((guessDisplacement-lowerBoundDisplacement)/(upperBoundDisplacement-lowerBoundDisplacement))-1.0);
		guessParameters[4] = guessTheta;
		guessParameters[5] = guessMu;

		// Seek optimal parameter configuration
//		org.apache.commons.math3.optimization.direct.BOBYQAOptimizer optimizer2 = new org.apache.commons.math3.optimization.direct.BOBYQAOptimizer(8);
		org.apache.commons.math3.optimization.direct.CMAESOptimizer optimizer2 = new org.apache.commons.math3.optimization.direct.CMAESOptimizer();

		double[] bestParameters = null;
		try {
			PointValuePair result = optimizer2.optimize(
					maxIterations,
					objectiveFunction,
					GoalType.MAXIMIZE,
					guessParameters /* start point */
					);
			bestParameters = result.getPoint();
		} catch(org.apache.commons.math3.exception.MathIllegalStateException e) {
			System.out.println("Solver failed");
			bestParameters = guessParameters;
		}

		// Transform parameters to GARCH parameters
		double omega	= Math.exp(bestParameters[0]);
		double mucorr	= Math.exp(-Math.exp(-bestParameters[1]));
		double muema	= Math.exp(-Math.exp(-bestParameters[2]));
		double beta		= mucorr * muema;
		double alpha	= mucorr - beta;
		double displacementNormed = 1.0/(1.0+Math.exp(-bestParameters[3]));
		double displacement = (upperBoundDisplacement-lowerBoundDisplacement)*displacementNormed+lowerBoundDisplacement;
		double theta	= bestParameters[4];
		double mu		= bestParameters[5];

		double[] quantiles = {0.01, 0.05, 0.5};
		double[] quantileValues = this.getQuantilPredictionsForParameters(theta, mu, omega, alpha, beta, displacement, quantiles);

		Map results = new HashMap();
		results.put("Omega", omega);
		results.put("Alpha", alpha);
		results.put("Beta", beta);
		results.put("Theta", theta);
		results.put("Mu", mu);
		results.put("Displacement", displacement);
		results.put("Likelihood", this.getLogLikelihoodForParameters(theta, mu, omega, alpha, beta, displacement));
		results.put("Vol", Math.sqrt(this.getLastResidualForParameters(theta, mu, omega, alpha, beta, displacement)));
		results.put("Quantile=1%", quantileValues[0]);
		results.put("Quantile=5%", quantileValues[1]);
		results.put("Quantile=50%", quantileValues[2]);
		return results;
	}

	private static double restrictToOpenSet(double value, double lowerBond, double upperBound) {
		value = Math.max(value, lowerBond  * (1.0+Math.signum(lowerBond)*1E-15) + 1E-15);
		value = Math.min(value, upperBound * (1.0-Math.signum(upperBound)*1E-15) - 1E-15);
		return value;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy