Permalink
Browse files

[PLAT-2038] Having trouble getting mixed log-normal model to converge

  • Loading branch information...
1 parent 7abbe8b commit c0ac1438747d6d61f5e53cc34c85852aa1cb8cfd Richard White committed Mar 26, 2012
Showing with 263 additions and 27 deletions.
  1. +1 −1 .../src/com/opengamma/financial/model/volatility/local/LocalVolatilityForwardPDEGreekCalculator.java
  2. +6 −0 ...cts/OG-Analytics/src/com/opengamma/financial/model/volatility/smile/fitting/SmileModelFitter.java
  3. +29 −0 ...com/opengamma/financial/model/volatility/smile/fitting/interpolation/CosineWeightingFunction.java
  4. +27 −0 ...com/opengamma/financial/model/volatility/smile/fitting/interpolation/LinearWeightingFunction.java
  5. +27 −0 ...c/com/opengamma/financial/model/volatility/smile/fitting/interpolation/SineWeightingFunction.java
  6. +35 −0 ...s/src/com/opengamma/financial/model/volatility/smile/fitting/interpolation/SurfaceArrayUtils.java
  7. +65 −0 ...s/src/com/opengamma/financial/model/volatility/smile/fitting/interpolation/WeightingFunction.java
  8. +1 −0 ...com/opengamma/financial/model/volatility/smile/fitting/sabr/LinearPiecewiseSABRSurfaceFitter.java
  9. +1 −0 .../opengamma/financial/model/volatility/smile/fitting/sabr/MoneynessPiecewiseSABRSurfaceFitter.java
  10. +8 −4 ...nalytics/src/com/opengamma/financial/model/volatility/smile/fitting/sabr/PiecewiseSABRFitter.java
  11. +1 −0 ...com/opengamma/financial/model/volatility/smile/fitting/sabr/StrikePiecewiseSABRSurfaceFitter.java
  12. +7 −0 ...nalytics/src/com/opengamma/financial/model/volatility/smile/function/MixedLogNormalModelData.java
  13. +15 −14 projects/OG-Analytics/src/com/opengamma/math/minimization/UncoupledParameterTransforms.java
  14. +9 −1 projects/OG-Analytics/src/com/opengamma/math/statistics/leastsquare/NonLinearLeastSquare.java
  15. +22 −6 ...cs/tests/unit/com/opengamma/financial/model/volatility/smile/fitting/PiecewiseSABRFitterTest.java
  16. +2 −0 ...unit/com/opengamma/financial/model/volatility/smile/fitting/sabr/CosineWeightingFunctionTest.java
  17. +2 −0 ...unit/com/opengamma/financial/model/volatility/smile/fitting/sabr/LinearWeightingFunctionTest.java
  18. +2 −0 ...ts/unit/com/opengamma/financial/model/volatility/smile/fitting/sabr/PiecewiseSABRFitter1Test.java
  19. +2 −0 ...s/unit/com/opengamma/financial/model/volatility/smile/fitting/sabr/WeightingFunctionTestCase.java
  20. +1 −1 ...om/opengamma/financial/analytics/model/volatility/local/ForexLocalVolatilityPDEPriceFunction.java
@@ -25,9 +25,9 @@
import com.opengamma.financial.model.interestrate.curve.ForwardCurve;
import com.opengamma.financial.model.option.pricing.analytic.formula.EuropeanVanillaOption;
import com.opengamma.financial.model.volatility.BlackFormulaRepository;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils;
import com.opengamma.financial.model.volatility.smile.fitting.sabr.PiecewiseSABRSurfaceFitter1;
import com.opengamma.financial.model.volatility.smile.fitting.sabr.SmileSurfaceDataBundle;
-import com.opengamma.financial.model.volatility.smile.fitting.sabr.SurfaceArrayUtils;
import com.opengamma.financial.model.volatility.surface.BlackVolatilitySurface;
import com.opengamma.financial.model.volatility.surface.LocalVolatilitySurfaceConverter;
import com.opengamma.financial.model.volatility.surface.LocalVolatilitySurfaceMoneyness;
@@ -37,6 +37,7 @@ public Boolean evaluate(final DoubleMatrix1D x) {
}
};
+ private final VolatilityFunctionProvider<T> _model;
private final Function1D<T, double[]> _volFunc;
private final Function1D<T, double[][]> _volAdjointFunc;
private final DoubleMatrix1D _marketValues;
@@ -67,6 +68,7 @@ public SmileModelFitter(final double forward, final double[] strikes, final doub
_volFunc = model.getVolatilityFunction(forward, strikes, timeToExpiry);
_volAdjointFunc = model.getModelAdjointFunction(forward, strikes, timeToExpiry);
+ _model = model;
}
/**
@@ -143,4 +145,8 @@ protected DoubleMatrix1D getMaximumStep() {
return UNCONSTRAINED;
}
+ public VolatilityFunctionProvider<T> getModel() {
+ return _model;
+ }
+
}
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
+ *
+ * Please see distribution for license.
+ */
+package com.opengamma.financial.model.volatility.smile.fitting.interpolation;
+
+
+
+/**
+ *
+ */
+public final class CosineWeightingFunction extends WeightingFunction {
+ private static final CosineWeightingFunction s_instance = new CosineWeightingFunction();
+
+ public static CosineWeightingFunction getInstance() {
+ return s_instance;
+ }
+
+ private CosineWeightingFunction() {
+ }
+
+ @Override
+ public double getWeight(final double y) {
+
+ final double cos = Math.cos(Math.PI / 2 * y);
+ return cos * cos;
+ }
+}
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
+ *
+ * Please see distribution for license.
+ */
+package com.opengamma.financial.model.volatility.smile.fitting.interpolation;
+
+
+
+/**
+ *
+ */
+public final class LinearWeightingFunction extends WeightingFunction {
+ private static final LinearWeightingFunction s_instance = new LinearWeightingFunction();
+
+ public static LinearWeightingFunction getInstance() {
+ return s_instance;
+ }
+
+ private LinearWeightingFunction() {
+ }
+
+ @Override
+ public double getWeight(final double y) {
+ return y;
+ }
+}
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
+ *
+ * Please see distribution for license.
+ */
+package com.opengamma.financial.model.volatility.smile.fitting.interpolation;
+
+
+/**
+ * TODO this belongs with interpolators
+ */
+public final class SineWeightingFunction extends WeightingFunction {
+ private static final SineWeightingFunction s_instance = new SineWeightingFunction();
+
+ public static SineWeightingFunction getInstance() {
+ return s_instance;
+ }
+
+ private SineWeightingFunction() {
+ }
+
+ @Override
+ public double getWeight(final double y) {
+ return 0.5 * (Math.sin(Math.PI * (y - 0.5)) + 1);
+ }
+
+}
@@ -0,0 +1,35 @@
+/**
+ * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
+ *
+ * Please see distribution for license.
+ */
+package com.opengamma.financial.model.volatility.smile.fitting.interpolation;
+
+import java.util.Arrays;
+
+/**
+ *
+ */
+public class SurfaceArrayUtils {
+
+ public static int getLowerBoundIndex(final double[] strikes, final double strike) {
+ final int n = strikes.length;
+ if (strike < strikes[0]) {
+ return 0;
+ }
+ if (strike > strikes[n - 1]) {
+ return n - 1;
+ }
+
+ int index = Arrays.binarySearch(strikes, strike);
+ if (index >= 0) {
+ // Fast break out if it's an exact match.
+ return index;
+ }
+ if (index < 0) {
+ index = -(index + 1);
+ index--;
+ }
+ return index;
+ }
+}
@@ -0,0 +1,65 @@
+/**
+ * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
+ *
+ * Please see distribution for license.
+ */
+package com.opengamma.financial.model.volatility.smile.fitting.interpolation;
+
+import com.opengamma.util.ArgumentChecker;
+
+/**
+ * A function to allow a smooth weighing between two functions. If two functions f(x) and g(x) fit the data set (x_i,y_i) at the points x_a and x_b
+ * (i.e. f(x_a) = g(x_a) = y_a and f(x_b) = g(x_b) = y_b), then a weighted function h(x) = w(x)f(x) + (1-w(x))*g(x) with 0 <= w(x) <= 1 will also
+ * fit the points a and b
+ * TODO this belongs with interpolator
+ */
+public abstract class WeightingFunction {
+
+ /**
+ * Get the function weight for point x
+ * @param xs All the independent data points
+ * @param x An arbitrary point
+ * @return The weight
+ */
+ public double getWeight(final double[] xs, final double x) {
+ ArgumentChecker.notNull(xs, "strikes");
+ final int index = SurfaceArrayUtils.getLowerBoundIndex(xs, x);
+ final double y = getY(xs, index, x);
+ return getWeight(y);
+ }
+
+ /**
+ * Get the function weight for point x - use this if index in known
+ * @param xs All the independent data points
+ * @param index The index of the data point below x
+ * @param x An arbitrary point
+ * @return The weight
+ */
+ public double getWeight(final double[] xs, final int index, final double x) {
+ ArgumentChecker.notNull(xs, "strikes");
+ final double y = getY(xs, index, x);
+ return getWeight(y);
+ }
+
+ /**
+ * For an arbitrary point x, let the two data points immediately below and above x be, x_a and x_b, then define y = (x_b - x)/(x_b - x_a).
+ * @param xs All the independent data points
+ * @param lowerBoundIndex The index of x_a
+ * @param x An arbitrary point
+ * @return y
+ */
+ private double getY(double[] xs, int lowerBoundIndex, double x) {
+ ArgumentChecker.notNull(xs, "strikes");
+ ArgumentChecker.notNegative(lowerBoundIndex, "index");
+ ArgumentChecker.isTrue(lowerBoundIndex <= xs.length - 2, "index cannot be larger than {}, have {}", xs.length - 2, lowerBoundIndex);
+ return (xs[lowerBoundIndex + 1] - x) / (xs[lowerBoundIndex + 1] - xs[lowerBoundIndex]);
+ }
+
+ /**
+ * The condition that must be satisfied by all weight functions is that w(1) = 1, w(0) = 0 and dw(y)/dy <= 0 - i.e. w(y) is monotonically decreasing
+ * @param y a value between 0 and 1
+ * @return The weight
+ */
+ public abstract double getWeight(double y);
+
+}
@@ -9,6 +9,7 @@
import org.apache.commons.lang.Validate;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils;
import com.opengamma.financial.model.volatility.surface.BlackVolatilitySurfaceStrike;
import com.opengamma.financial.model.volatility.surface.Strike;
import com.opengamma.math.MathException;
@@ -10,6 +10,7 @@
import java.util.Arrays;
import com.opengamma.financial.model.interestrate.curve.ForwardCurve;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils;
import com.opengamma.financial.model.volatility.surface.BlackVolatilitySurfaceMoneyness;
import com.opengamma.financial.model.volatility.surface.Moneyness;
import com.opengamma.math.function.Function;
@@ -13,6 +13,9 @@
import com.opengamma.financial.model.volatility.smile.fitting.SABRModelFitter;
import com.opengamma.financial.model.volatility.smile.fitting.SmileModelFitter;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.SineWeightingFunction;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.WeightingFunction;
import com.opengamma.financial.model.volatility.smile.function.SABRFormulaData;
import com.opengamma.financial.model.volatility.smile.function.SABRHaganVolatilityFunction;
import com.opengamma.math.function.Function1D;
@@ -54,7 +57,7 @@ public PiecewiseSABRFitter(final double defaultBeta, final WeightingFunction wei
_globalBetaSearch = false;
}
- public final SABRFormulaData[] getFittedfModelParameters(final double forward, final double[] strikes, final double expiry, final double[] impliedVols) {
+ public final SABRFormulaData[] getFittedModelParameters(final double forward, final double[] strikes, final double expiry, final double[] impliedVols) {
ArgumentChecker.notNull(strikes, "strikes");
ArgumentChecker.notNull(impliedVols, "implied volatilities");
final int n = strikes.length;
@@ -76,8 +79,9 @@ public PiecewiseSABRFitter(final double defaultBeta, final WeightingFunction wei
DoubleMatrix1D start;
//almost flat surface
- if (averageVol2 / averageVol < 0.01) {
- start = new DoubleMatrix1D(averageVol, 1.0, 0.0, 0.0);
+ if (averageVol2 / averageVol < 0.01) { //TODO remove the false before put back
+ final double approxAlpha = averageVol * Math.pow(forward, 0.01);
+ start = new DoubleMatrix1D(approxAlpha, 0.99, 0.0, 0.001);
if (!_globalBetaSearch && _defaultBeta != 1.0) {
s_logger.warn("Smile almost flat. Cannot use beta = ", +_defaultBeta + " so ignored");
}
@@ -129,7 +133,7 @@ public PiecewiseSABRFitter(final double defaultBeta, final WeightingFunction wei
public Function1D<Double, Double> getVolatilityFunction(final double forward, final double[] strikes, final double expiry, final double[] impliedVols) {
- final SABRFormulaData[] modelParams = getFittedfModelParameters(forward, strikes, expiry, impliedVols);
+ final SABRFormulaData[] modelParams = getFittedModelParameters(forward, strikes, expiry, impliedVols);
final int n = strikes.length;
return new Function1D<Double, Double>() {
@@ -10,6 +10,7 @@
import org.apache.commons.lang.Validate;
import com.opengamma.financial.model.interestrate.curve.ForwardCurve;
+import com.opengamma.financial.model.volatility.smile.fitting.interpolation.SurfaceArrayUtils;
import com.opengamma.financial.model.volatility.surface.BlackVolatilitySurfaceStrike;
import com.opengamma.financial.model.volatility.surface.Strike;
import com.opengamma.math.function.Function;
@@ -285,4 +285,11 @@ public boolean equals(Object obj) {
return true;
}
+ @Override
+ public String toString() {
+ return "MixedLogNormalModelData [_sigmas=" + Arrays.toString(_sigmas) + ", _w=" + Arrays.toString(_w) + ", _f=" + Arrays.toString(_f) + "]";
+ }
+
+
+
}
@@ -13,12 +13,13 @@
import com.opengamma.math.matrix.DoubleMatrix1D;
import com.opengamma.math.matrix.DoubleMatrix2D;
+import com.opengamma.util.ArgumentChecker;
/**
* For a set of <i>n</i> function parameters, this takes <i>n</i> ParameterLimitsTransform (which can be the NullTransform which does NOT transform the parameter) which transform
* a constrained function parameter (e.g. must be between -1 and 1) to a unconstrained fit parameter. It also takes a BitSet (of length <i>n</i>) with an element set to <b>true</b> if
* that parameter is fixed - a set of <i>n</i> startValues must also be provided, with only those corresponding to fixed parameters being used (i.e. the parameter is fixed at the startValue).
- * The purpose is to allow an optimiser to work with unconstrained parameters without modifying the function that one wishes to optimise.
+ * The purpose is to allow an optimiser to work with unconstrained parameters without modifying the function that one wishes to optimise.
*/
// TODO not tested
public class UncoupledParameterTransforms implements NonLinearParameterTransforms {
@@ -40,7 +41,7 @@ public UncoupledParameterTransforms(final DoubleMatrix1D startValues, final Para
Validate.notEmpty(transforms, "must specify transforms");
Validate.notNull(fixed, "must specify what is fixed (even if none)");
_nMP = startValues.getNumberOfElements();
- Validate.isTrue(_nMP == transforms.length, "must give a transform for each model parameter");
+ ArgumentChecker.isTrue(_nMP == transforms.length, "Have {}-dimensional start value but {} transforms", _nMP, transforms.length);
final int count = fixed.cardinality();
Validate.isTrue(count < _nMP, "all parameters are fixed");
_nFP = _nMP - count;
@@ -51,7 +52,7 @@ public UncoupledParameterTransforms(final DoubleMatrix1D startValues, final Para
/**
*
- * @return The number of function parameters
+ * @return The number of function parameters
*/
@Override
public int getNumberOfModelParameters() {
@@ -60,7 +61,7 @@ public int getNumberOfModelParameters() {
/**
*
- * @return The number of fitting parameters (equals the number of model parameters minus the number of fixed parameters)
+ * @return The number of fitting parameters (equals the number of model parameters minus the number of fixed parameters)
*/
@Override
public int getNumberOfFittingParameters() {
@@ -69,8 +70,8 @@ public int getNumberOfFittingParameters() {
/**
* Transforms from a set of function parameters (some of which may have constrained range and/or be fixed) to a (possibly smaller) set of unconstrained fitting parameters
- * <b>Note:</b> If a parameter is fixed, it is its value as provided by <i>startValues<\i> not the value given here that will be returned by inverseTransform (and thus used in the function)
- * @param functionParameters The function parameters
+ * <b>Note:</b> If a parameter is fixed, it is its value as provided by <i>startValues<\i> not the value given here that will be returned by inverseTransform (and thus used in the function)
+ * @param functionParameters The function parameters
* @return The fitting parameters
*/
@Override
@@ -90,7 +91,7 @@ public DoubleMatrix1D transform(final DoubleMatrix1D functionParameters) {
/**
* Transforms from a set of unconstrained fitting parameters to a (possibly larger) set of function parameters (some of which may have constrained range and/or be fixed).
* @param fittingParameters The fitting parameters
- * @return The function parameters
+ * @return The function parameters
*/
@Override
public DoubleMatrix1D inverseTransform(final DoubleMatrix1D fittingParameters) {
@@ -109,10 +110,10 @@ public DoubleMatrix1D inverseTransform(final DoubleMatrix1D fittingParameters) {
}
/**
- * Calculates the Jacobian of the transform from function parameters to fitting parameters - the i,j element will be the partial derivative of i^th fitting parameter with respect
- * to the j^th function parameter
- * @param functionParameters The function parameters
- * @return matrix of partial derivative of fitting parameter with respect to function parameters
+ * Calculates the Jacobian of the transform from function parameters to fitting parameters - the i,j element will be the partial derivative of i^th fitting parameter with respect
+ * to the j^th function parameter
+ * @param functionParameters The function parameters
+ * @return matrix of partial derivative of fitting parameter with respect to function parameters
*/
// TODO not tested
@Override
@@ -130,10 +131,10 @@ public DoubleMatrix2D jacobian(final DoubleMatrix1D functionParameters) {
}
/**
- * Calculated the Jacobian of the transform from fitting parameters to function parameters - the i,j element will be the partial derivative of i^th function parameter with respect
- * to the j^th fitting parameter
+ * Calculated the Jacobian of the transform from fitting parameters to function parameters - the i,j element will be the partial derivative of i^th function parameter with respect
+ * to the j^th fitting parameter
* @param fittingParameters The fitting parameters
- * @return matrix of partial derivative of function parameter with respect to fitting parameters
+ * @return matrix of partial derivative of function parameter with respect to fitting parameters
*/
// TODO not tested
@Override
Oops, something went wrong.

0 comments on commit c0ac143

Please sign in to comment.