/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.math.impl.statistics.descriptive; import com.opengamma.strata.collect.ArgChecker; import com.opengamma.strata.collect.array.DoubleArray; /** * Implementation of a quantile estimator. * <p> * The quantile is linearly interpolated between two sample values. The probability dimension <i>p<subscript>i</subscript> * on which the interpolation take place (X axis) varies between actual implementation of the abstract class. * For each probability <i>p<subscript>i</subscript></i>, the cumulative distribution value is the sample value with * same index. The index used above are the Java index plus 1. * <p> * Reference: Value-At-Risk, OpenGamma Documentation 31, Version 0.1, April 2015. */ public abstract class InterpolationQuantileMethod extends QuantileCalculationMethod { @Override protected double quantile(double level, DoubleArray sortedSample, boolean isExtrapolated) { ArgChecker.isTrue(level > 0, "Quantile should be above 0."); ArgChecker.isTrue(level < 1, "Quantile should be below 1."); int sampleSize = sortedSample.size(); double adjustedLevel = checkIndex(level * sampleCorrection(sampleSize) + indexCorrection(), sortedSample.size(), isExtrapolated); int lowerIndex = (int) Math.floor(adjustedLevel); int upperIndex = (int) Math.ceil(adjustedLevel); double lowerWeight = upperIndex - adjustedLevel; double upperWeight = 1d - lowerWeight; return lowerWeight * sortedSample.get(lowerIndex - 1) + upperWeight * sortedSample.get(upperIndex - 1); } @Override protected double expectedShortfall(double level, DoubleArray sortedSample) { ArgChecker.isTrue(level > 0, "Quantile should be above 0."); ArgChecker.isTrue(level < 1, "Quantile should be below 1."); int sampleSize = sampleCorrection(sortedSample.size()); double fractionalIndex = level * sampleSize + indexCorrection(); double adjustedLevel = checkIndex(fractionalIndex, sortedSample.size(), true); int lowerIndex = (int) Math.floor(adjustedLevel); int upperIndex = (int) Math.ceil(adjustedLevel); double interval = 1d / (double) sampleSize; double losses = sortedSample.get(0) * interval * (Math.min(fractionalIndex, 1d) - indexCorrection()); for (int i = 0; i < lowerIndex - 1; i++) { losses += 0.5 * (sortedSample.get(i) + sortedSample.get(i + 1)) * interval; } if (lowerIndex != upperIndex) { double lowerWeight = upperIndex - adjustedLevel; double upperWeight = 1d - lowerWeight; double quantile = lowerWeight * sortedSample.get(lowerIndex - 1) + upperWeight * sortedSample.get(upperIndex - 1); losses += 0.5 * (sortedSample.get(lowerIndex - 1) + quantile) * interval * upperWeight; } if (fractionalIndex > sortedSample.size()) { losses += sortedSample.get(sortedSample.size() - 1) * (fractionalIndex - sortedSample.size()) * interval; } return losses / level; } //------------------------------------------------------------------------- /** * Internal method returning the index correction for the specific implementation. * * @return the correction */ abstract double indexCorrection(); /** * Internal method returning the sample size correction for the specific implementation. * * @param sampleSize the sample size * @return the correction */ abstract int sampleCorrection(int sampleSize); }