Error calibrating curve

#1

Hi . I followed the SmithWilsonCurveFunction example class and created a class with thw necessary functions for the Nelson- Siegel 4 parameter function. I get an error I cannot trace : Function returned array of incorrect length 4, expected 13 when I call calibrate. Am I overlooking something? Thanks very much.

Oyvind

private static final NelsonSiegelCurveFunction NS_CURVE = NelsonSiegelCurveFunction.DEFAULT;

  private static final BiFunction<DoubleArray, Double, Double> VALUE_FUNCTION1 = 
		  new BiFunction<DoubleArray, Double, Double>() {
    @Override
    public Double apply(DoubleArray t, Double u) {
    	// t= params, u = x
      return NS_CURVE.value(u, t);
    }
  };
  private static final BiFunction<DoubleArray, Double, Double> DERIVATIVE_FUNCTION1 =
      new BiFunction<DoubleArray, Double, Double>() {
        @Override
        public Double apply(DoubleArray t, Double u) {
          return NS_CURVE.firstDerivative(u,  t);
        }
      };
  private static final BiFunction<DoubleArray, Double, DoubleArray> SENSI_FUNCTION1 =
      new BiFunction<DoubleArray, Double, DoubleArray>() {
        @Override
        public DoubleArray apply(DoubleArray t, Double u) {
          return NS_CURVE.parameterSensitivity(u,t);
        }
      };
#2

That error seems to comes from DoubleMatrix.ofArrays and would happen if the function does not return an array of the size expected (ie. the number of columns passed as argument 2). However I can find no code that uses it, so I’m afraid I don’t have enough info to help. You’ll need to debug and see what code is calling the method, and why that code is passing 13 as the number of columns. If I were to take a random guess, perhaps 13 is the number of indices in the data you are trying to calibrate?

#3

Hi and thanks for the reply. More explanation:
Here is the instantiation of the ParameterizedFunctionalCurveDefinition class:

private static final ParameterizedFunctionalCurveDefinition CURVE_DEFN2 = ParameterizedFunctionalCurveDefinition.builder()
				      .name(CURVE_NAME2)
				      .xValueType(ValueType.YEAR_FRACTION)
				      .yValueType(ValueType.DISCOUNT_FACTOR)
				      .dayCount(CURVE_DC)
				      .initialGuess(DoubleArray.of(0d,0d,0d,0d).toList())
				      .valueFunction(VALUE_FUNCTION1)
				      .derivativeFunction(DERIVATIVE_FUNCTION1)
				      .sensitivityFunction(SENSI_FUNCTION1)
				      .nodes(ALL_NODES)
				      .parameterMetadata(ParameterMetadata.listOfEmpty(4))
				      .build(); 

My understanding is that a curve can be defined using the valueFunction (returning a double for a x), a derivative function (returning a double ) and a sensitivity function (returning a double for each parameter for a x ). This curvedefininition, data and the indicies are supplied (in my case 13) to the calibration. Unfortunately this appealing approach does not seem to work for me. Have I set up my functions (especially the sensitivityfunction) right? The SmithWilson Test example is a case where number of indicies are equal to parameters. In my case I have four parameters and 13 indicies.

The error message
 Exception in thread "main" java.lang.IllegalArgumentException: Function returned array of incorrect length 4, expected 13
    	at com.opengamma.strata.collect.array.DoubleMatrix.ofArrayObjects(DoubleMatrix.java:182)
    	at com.opengamma.strata.pricer.curve.CalibrationDerivative.apply(CalibrationDerivative.java:72)
    	at com.opengamma.strata.pricer.curve.CalibrationDerivative.apply(CalibrationDerivative.java:24)
    	at com.opengamma.strata.math.impl.rootfinding.newton.JacobianEstimateInitializationFunction.getInitializedMatrix(JacobianEstimateInitializationFunction.java:26)
    	at com.opengamma.strata.math.impl.rootfinding.newton.BaseNewtonVectorRootFinder.findRoot(BaseNewtonVectorRootFinder.java:87)
    	at com.opengamma.strata.pricer.curve.RatesCurveCalibrator.calibrateGroup(RatesCurveCalibrator.java:318)
    	at com.opengamma.strata.pricer.curve.RatesCurveCalibrator.calibrate(RatesCurveCalibrator.java:276)
    	at com.opengamma.strata.pricer.curve.RatesCurveCalibrator.calibrate(RatesCurveCalibrator.java:226)
    	at com.oyvind.RatesLoader.eiopa(RatesLoader.java:315)
    	at com.oyvind.RatesLoader.main(RatesLoader.java:95)
#4

Have you seen com.opengamma.strata.pricer.curve.CalibrationDiscountingSmithWilsonTest ? Looks like it might be doing exactly what you want using com.opengamma.strata.math.impl.interpolation.SmithWilsonCurveFunction? Perhaps they can help solve the problem.

I also note this Javadoc on ParameterizedFunctionalCurveDefinition which may be relevant:

The number of the curve parameters is in general different from the number of the instruments.
However, the number mismatch tends to cause the root-finding failure in the curve calibration.
#5

Hi and thanks,
I am still lost. When I try to run calibrate on the example data in the ParameterizedFunctionalCurveDefinitionTest I also get the error. I still hope I am overlooking something… Maybe this functionality is not a priority, but its very useful when dealing with all kinds of regulatory imposed curves (Such as the smith wilson example which works great)
KR Oyvind

#6

As I said, I don’t really have enough to go on here. The SmithWilson function insists on the number of parameters being the same size as the number of nodes. From what you’ve written, I assume you don’t have that in your code, but its not clear why that causes a problem.

If you want me to look further, I think you’d need to create a fully self-contained example, such as a public GitHub project or zip file. Without the actual code and test that is failing I can’t really proceed.
Stephen

#7

That is a good suggestion. Please find attached a link to zip with a test project. In the RatesLoader class I try to calibrate one curve based on the ParameterizedFunctionalCurveDefinitionTest class (esr2 method), one using a class I defined for the Nelson Siegel (esr method). Also in the RatesLoader class the example calibration of the Smith Wilson is included (eiopa method). Only this last example is working right now.
I am working behind firewall, so you will need to uncomment one dependency in the pom. Its the renjin package I use for quotes data. Pease let me know if you have any problems with the files.
Edit: Fixed a classpath reference in ln 206, RatesLoader. Should have been similar to ln 183 same class.

Link: https://1drv.ms/u/s!AqXlasJAch6Dhb4MEBgFjgyFlaI1rg

#8

OK, so having taken a look and asked our quants, the basic answer is that what you want is not supported. The curve calibration in Strata requires the number of parameters to be the same as the number of nodes (trades) in the curve. This is to ensure that the calibration can actually result in an answer. Trying to exactly calibrate 28 points from 4 parameters is simply not practical. Specifically, Strata requires each node to be exactly met for the calibration to complete, and that isn’t viable given the mismatch between the parameters and the nodes.

I’m informed that it is possible to do best fit (rather than exact) calibration, however Strata does not support this (nor is it likely to be a direction we’d take it).

So, you can get your code to return a result by reducing the number of nodes (trades) in the curve to 4. I’ve tried that and the code does complete, although I suspect that the calibration is not ideal. If you want to do something like this you would need to calibrate outside Strata and pass in the resulting curve.

On the upside, I did find a performance tweak I can make to calibration while investigating this!

HTH
Stephen

#9

Hi Stephen,
Thanks a lot that you looked into this. It makes absolutely sense that Strata has chosen this approach. From what I understand my suggested interpolation and the subsequent extrapolation based on the fit is not consistent with the market.
KR Oyvind