net.finmath.timeseries.models.parametric.DisplacedLognormalARMAGARCH Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of finmath-lib Show documentation
Show all versions of finmath-lib Show documentation
finmath lib is a Mathematical Finance Library in Java.
It provides algorithms and methodologies related to mathematical finance.
The newest version!
/*
* (c) Copyright Christian P. Fries, Germany. Contact: [email protected].
*
* Created on 15.07.2012
*/
package net.finmath.timeseries.models.parametric;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.math3.analysis.MultivariateFunction;
import org.apache.commons.math3.optim.SimplePointChecker;
import org.apache.commons.math3.random.MersenneTwister;
import net.finmath.optimizer.LevenbergMarquardt;
import net.finmath.optimizer.Optimizer;
import net.finmath.optimizer.SolverException;
import net.finmath.timeseries.HistoricalSimulationModel;
import net.finmath.timeseries.TimeSeries;
import net.finmath.timeseries.TimeSeriesModelParametric;
import net.finmath.timeseries.TimeSeriesView;
/**
* Displaced log-normal process with ARMAGARCH(1,1) volatility.
*
* This class estimate the process
* \[
* \mathrm{d} \log(X + a) = \frac{\sigma}{b + a} \mathrm{d}W(t)
* \]
* where \( a > -min(X(t_{i}) \) and thus \( X+a > 0 \) and \( b = 1 - -min(X(t_{i}) \) \) and
* \( \sigma \) is given by a ARMAGARCH(1,1) process.
*
* The choice of b ensures that b+a ≥ 1.
* For a=0 we have a log-normal process with volatility σ/(b + a).
* For a=infinity we have a normal process with volatility σ.
*
* @author Christian Fries
* @version 1.0
*/
public class DisplacedLognormalARMAGARCH implements TimeSeriesModelParametric, HistoricalSimulationModel {
private final TimeSeries timeSeries;
private final double lowerBoundDisplacement;
private double upperBoundDisplacement = 10000000;
private final int maxIterations = 10000000;
/*
* Model properties
*/
private final String[] parameterNames = new String[] { "omega", "alpha", "beta", "displacement", "theta", "mu" };
private final double[] parameterGuess = new double[] { 0.10, 0.2, 0.2, 10.0, 0.0, 0.0 };
private final double[] parameterStep = new double[] { 0.01, 0.1, 0.1, 5.0, 0.1, 0.1 };
private final double[] lowerBound;
private final double[] upperBound;
public DisplacedLognormalARMAGARCH(final TimeSeries timeSeries) {
this(timeSeries, -Double.MAX_VALUE);
}
public DisplacedLognormalARMAGARCH(final TimeSeries timeSeries, final double lowerBoundDisplacement) {
this.timeSeries = timeSeries;
double valuesMin = Double.MAX_VALUE;
for(final double value : timeSeries.getValues()) {
valuesMin = Math.min(value, valuesMin);
}
this.lowerBoundDisplacement = Math.max(-valuesMin+1,lowerBoundDisplacement);
lowerBound = new double[] { 0, 0, 0, this.lowerBoundDisplacement, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY };
upperBound = new double[] { Double.POSITIVE_INFINITY, 1, 1, upperBoundDisplacement, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY };
}
public DisplacedLognormalARMAGARCH(final TimeSeries timeSeries, final double lowerBoundDisplacement, final double upperBoundDisplacement) {
this.timeSeries = timeSeries;
double valuesMin = Double.MAX_VALUE;
for(final double value : timeSeries.getValues()) {
valuesMin = Math.min(value, valuesMin);
}
this.lowerBoundDisplacement = Math.max(-valuesMin+1,lowerBoundDisplacement);
this.upperBoundDisplacement = Math.max(this.lowerBoundDisplacement,upperBoundDisplacement);
lowerBound = new double[] { 0, 0, 0, 0, 0, this.lowerBoundDisplacement };
upperBound = new double[] { Double.POSITIVE_INFINITY, 1, 1, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, this.upperBoundDisplacement };
}
public double getLogLikelihoodForParameters(final double[] parameters)
{
final double omega = parameters[0];
final double alpha = parameters[1];
final double beta = parameters[2];
final double displacement = parameters[3];
final double theta = parameters[4];
final double mu = parameters[5];
double logLikelihood = 0.0;
final double volScaling = (1+Math.abs(displacement));
double evalPrev = 0.0;
double eval = volScaling * (Math.log((timeSeries.getValue(1)+displacement)/(timeSeries.getValue(0)+displacement)));
double h = omega / (1.0 - alpha - beta);
double m = 0.0; // xxx how to init?
final int length = timeSeries.getNumberOfTimePoints();
for (int i = 1; i < length-1; i++) {
m = -theta * m + eval - mu * evalPrev;
h = (omega + alpha * m * m) + beta * h;
final double value1 = timeSeries.getValue(i);
final double value2 = timeSeries.getValue(i+1);
final double evalNext = volScaling * (Math.log((value2+displacement)/(value1+displacement)));
final double mNext = -theta * m + evalNext - mu * eval;
logLikelihood += - Math.log(h) - 2 * Math.log((value2+displacement)/volScaling) - mNext* mNext / h;
evalPrev = eval;
eval = evalNext;
}
logLikelihood += - Math.log(2 * Math.PI) * (length);
logLikelihood *= 0.5;
return logLikelihood;
}
public double getLastResidualForParameters(final double[] parameters) {
final double omega = parameters[0];
final double alpha = parameters[1];
final double beta = parameters[2];
final double displacement = parameters[3];
final double theta = parameters[4];
final double mu = parameters[5];
double evalPrev = 0.0;
final double volScaling = (1+Math.abs(displacement));
double h = omega / (1.0 - alpha - beta);
double m = 0.0; // xxx how to init?
final int length = timeSeries.getNumberOfTimePoints();
for (int i = 1; i < length-1; i++) {
final double eval = volScaling * (Math.log((timeSeries.getValue(i)+displacement)/(timeSeries.getValue(i-1)+displacement)));
m = -theta * m + eval - mu * evalPrev;
h = (omega + alpha * m * m) + beta * h;
evalPrev = eval;
}
return h;
}
public double[] getSzenarios(final double[] parameters) {
final double omega = parameters[0];
final double alpha = parameters[1];
final double beta = parameters[2];
final double displacement = parameters[3];
final double theta = parameters[4];
final double mu = parameters[5];
final double[] szenarios = new double[timeSeries.getNumberOfTimePoints()-1];
final double volScaling = (1+Math.abs(displacement));
double evalPrev = 0.0;
double h = omega / (1.0 - alpha - beta);
double m = 0.0;
double vol = Math.sqrt(h) / volScaling;
for (int i = 1; i <= timeSeries.getNumberOfTimePoints()-1; i++) {
final double y = Math.log((timeSeries.getValue(i)+displacement)/(timeSeries.getValue(i-1)+displacement));
final double eval = volScaling * y;
m = eval - theta * m - mu * evalPrev;
szenarios[i-1] = m / vol / volScaling;
h = (omega + alpha * m * m) + beta * h;
vol = Math.sqrt(h) / volScaling;
evalPrev = eval;
}
java.util.Arrays.sort(szenarios);
return szenarios;
}
@Override
public Map getBestParameters() {
return getBestParameters(null);
}
/* (non-Javadoc)
* @see net.finmath.timeseries.HistoricalSimulationModel#getBestParameters(java.util.Map)
*/
@Override
public Map getBestParameters(final Map guess) {
// Create the objective function for the solver
class GARCHMaxLikelihoodFunction implements MultivariateFunction, Serializable {
private static final long serialVersionUID = 7072187082052755854L;
@Override
public double value(final double[] variables) {
final double omega = variables[0];
final double alpha = variables[1];
final double beta = variables[2];
final double displacement = variables[3];
final double theta = variables[4];
final double mu = variables[5];
double logLikelihood = getLogLikelihoodForParameters(variables);
// 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;
return logLikelihood;
}
}
final GARCHMaxLikelihoodFunction objectiveFunction = new GARCHMaxLikelihoodFunction();
// Create a guess for the solver
final double[] guessParameters = new double[6];
System.arraycopy(parameterGuess, 0, guessParameters, 0, parameterGuess.length);
if(guess != null) {
// A guess was provided, use that one
guessParameters[0] = (Double)guess.get("Omega");
guessParameters[1] = (Double)guess.get("Alpha");
guessParameters[2] = (Double)guess.get("Beta");
guessParameters[3] = (Double)guess.get("Displacement");
guessParameters[4] = (Double)guess.get("Theta");
guessParameters[5] = (Double)guess.get("Mu");
}
// Seek optimal parameter configuration
final Optimizer lm = new LevenbergMarquardt(guessParameters, new double[] { 1000.0 }, maxIterations*100, 2) {
private static final long serialVersionUID = -8844232820888815090L;
@Override
public void setValues(final double[] arg0, final double[] arg1) {
arg1[0] = objectiveFunction.value(arg0);
}
};
double[] bestParameters = null;
final boolean isUseLM = false;
if(isUseLM) {
try {
lm.run();
} catch (final SolverException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
bestParameters = lm.getBestFitParameters();
}
else {
final org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer optimizer2 = new org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer(maxIterations, Double.POSITIVE_INFINITY, true, 0, 0, new MersenneTwister(), false, new SimplePointChecker(0, 0))
{
@Override
public double computeObjectiveValue(final double[] params) {
return objectiveFunction.value(params);
}
/* (non-Javadoc)
* @see org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer#getGoalType()
*/
@Override
public org.apache.commons.math3.optim.nonlinear.scalar.GoalType getGoalType() {
// TODO Auto-generated method stub
return org.apache.commons.math3.optim.nonlinear.scalar.GoalType.MAXIMIZE;
}
/* (non-Javadoc)
* @see org.apache.commons.math3.optim.BaseMultivariateOptimizer#getStartPoint()
*/
@Override
public double[] getStartPoint() {
return guessParameters;
}
/* (non-Javadoc)
* @see org.apache.commons.math3.optim.BaseMultivariateOptimizer#getLowerBound()
*/
@Override
public double[] getLowerBound() {
return lowerBound;
}
/* (non-Javadoc)
* @see org.apache.commons.math3.optim.BaseMultivariateOptimizer#getUpperBound()
*/
@Override
public double[] getUpperBound() {
return upperBound;
}
};
try {
final org.apache.commons.math3.optim.PointValuePair result = optimizer2.optimize(
new org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer.PopulationSize((int) (4 + 3 * Math.log(guessParameters.length))),
new org.apache.commons.math3.optim.nonlinear.scalar.noderiv.CMAESOptimizer.Sigma(parameterStep)
);
bestParameters = result.getPoint();
} catch(final org.apache.commons.math3.exception.MathIllegalStateException e) {
System.out.println("Solver failed");
bestParameters = guessParameters;
}
}
// Transform parameters to GARCH parameters
final double omega = bestParameters[0];
final double alpha = bestParameters[1];
final double beta = bestParameters[2];
final double displacement = bestParameters[3];
final double theta = bestParameters[4];
final double mu = bestParameters[5];
final Map results = new HashMap<>();
results.put("parameters", bestParameters);
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("Szenarios", this.getSzenarios(bestParameters));
results.put("Likelihood", this.getLogLikelihoodForParameters(bestParameters));
results.put("Vol", Math.sqrt(this.getLastResidualForParameters(bestParameters)));
System.out.println(results.get("Likelihood") + "\t" + Arrays.toString(bestParameters));
return results;
}
private static double restrictToOpenSet(double value, final double lowerBond, final 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;
}
@Override
public TimeSeriesModelParametric getCloneCalibrated(final TimeSeries timeSeries) {
return new DisplacedLognormalARMAGARCH(timeSeries);
}
@Override
public HistoricalSimulationModel getCloneWithWindow(final int windowIndexStart, final int windowIndexEnd) {
return new DisplacedLognormalARMAGARCH(new TimeSeriesView(timeSeries, windowIndexStart, windowIndexEnd));
}
@Override
public double[] getParameters() {
return (double[])getBestParameters().get("parameters");
}
@Override
public String[] getParameterNames() {
return parameterNames;
}
}