/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.web.server.conversion;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.opengamma.analytics.financial.model.volatility.surface.VolatilitySurface;
import com.opengamma.analytics.math.surface.DoublesSurface;
import com.opengamma.analytics.math.surface.Surface;
import com.opengamma.engine.value.ValueSpecification;
/**
* Converter for {@link VolatilitySurface} objects.
* The difference between this form and VolatilitySurfaceData is that the latter
* will often be full of repeated values. Here we compute the distinct set of axes values,
* then query the surface for the z values.
*
* TODO PLAT-2249 Add field to allow transposing the display surface
*/
public class VolatilitySurfaceConverter implements ResultConverter<VolatilitySurface> {
@Override
/**
* Put together a map of results such that VolatilitySurfaceDetail.js can render it
* Required outputs:
* <p>'xs' and 'ys' - String arrays for axes labels.
* <p>'surface' - 2D double array with size matching xs and ys
*/
public Object convertForDisplay(ResultConverterCache context, ValueSpecification valueSpec, VolatilitySurface value, ConversionMode mode) {
Map<String, Object> result = new HashMap<String, Object>();
Surface<Double, Double, Double> inputSurface = value.getSurface();
if (inputSurface instanceof DoublesSurface) {
// Compute unique X data
Double[] xData = inputSurface.getXData();
DoubleArrayList uniqueXs = new DoubleArrayList();
for (int i = 0; i < xData.length; i++) {
if (!uniqueXs.contains(xData[i])) {
uniqueXs.add(xData[i]);
}
}
final int xCount = uniqueXs.size();
result.put("xCount", xCount);
// Compute unique Y data
Double[] yData = inputSurface.getYData();
DoubleArrayList uniqueYs = new DoubleArrayList();
for (int j = 0; j < yData.length; j++) {
if (!uniqueYs.contains(yData[j])) {
uniqueYs.add(yData[j]);
}
}
Collections.sort(uniqueYs);
final int yCount = uniqueYs.size();
result.put("yCount", yCount);
// Convert for display
if (mode == ConversionMode.FULL) {
String[] xStrings = new String[xCount];
for (int i = 0; i < xCount; i++) {
xStrings[i] = uniqueXs.get(i).toString();
}
result.put("xs", xStrings);
String[] ysStrings = new String[yCount];
for (int i = 0; i < yCount; i++) {
ysStrings[i] = uniqueYs.get(i).toString();
}
result.put("ys", ysStrings);
double[][] outputSurface = new double[yCount][xCount];
boolean[][] missingValues = new boolean[yCount][xCount];
// Summary view includes only the actual points of the surface
for (int y = 0; y < yCount; y++) {
for (int x = 0; x < xCount; x++) {
Double volatility = inputSurface.getZValue(uniqueXs.get(x), uniqueYs.get(y));
if (volatility == null) {
missingValues[y][x] = true;
//Some 'obviously wrong' value in case client displays it. Can't use NaN
outputSurface[y][x] = Double.MAX_VALUE;
} else {
outputSurface[y][x] = volatility;
}
}
}
result.put("surface", outputSurface);
result.put("missingValues", missingValues);
}
} else {
result.put("summary", "Converter only displays VolatilitySurface(DoublesSurface) as we require axes data");
}
return result;
}
@Override
public Object convertForHistory(ResultConverterCache context,
ValueSpecification valueSpec, VolatilitySurface value) {
// TODO Auto-generated method stub
return null;
}
@Override
public String convertToText(ResultConverterCache context, ValueSpecification valueSpec, VolatilitySurface value) {
// Could actually serialise the surface to a string if this is an issue
return "Volatility Surface (" + value.getSurface().getXData().length + " x " + value.getSurface().getYData().length + ")";
}
@Override
public String getFormatterName() {
return "SURFACE_DATA";
}
}