com.opengamma.strata.math.impl.statistics.descriptive.ExponentiallyWeightedInterpolationQuantileMethod Maven / Gradle / Ivy
Show all versions of strata-math Show documentation
/*
* 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.DoubleArrayMath;
import com.opengamma.strata.collect.array.DoubleArray;
/**
* Implementation of a quantile and expected shortfall estimator for series with exponentially weighted probabilities.
*
* Reference: "Value-at-risk", OpenGamma Documentation 31, Version 0.2, January 2016. Section A.4.
*/
public final class ExponentiallyWeightedInterpolationQuantileMethod
extends QuantileCalculationMethod {
/** The exponential weight. */
private final double lambda;
/**
* Constructor.
*
* The exponential weight lambda must be > 0 and < 1.0.
*
* @param lambda the exponential weight
*/
public ExponentiallyWeightedInterpolationQuantileMethod(double lambda) {
ArgChecker.inRangeExclusive(lambda, 0.0d, 1.0d, "exponential weight");
this.lambda = lambda;
}
@Override
public QuantileResult quantileResultFromUnsorted(double level, DoubleArray sample) {
return quantileDetails(level, sample, false, false);
}
@Override
public QuantileResult quantileResultWithExtrapolationFromUnsorted(double level, DoubleArray sample) {
return quantileDetails(level, sample, true, false);
}
@Override
public double quantileFromUnsorted(double level, DoubleArray sample) {
return quantileResultFromUnsorted(level, sample).getValue();
}
@Override
public double quantileWithExtrapolationFromUnsorted(double level, DoubleArray sample) {
return quantileResultWithExtrapolationFromUnsorted(level, sample).getValue();
}
@Override
public QuantileResult expectedShortfallResultFromUnsorted(double level, DoubleArray sample) {
return quantileDetails(level, sample, true, true);
}
@Override
public double expectedShortfallFromUnsorted(double level, DoubleArray sample) {
return expectedShortfallResultFromUnsorted(level, sample).getValue();
}
/**
* Compute the quantile estimation and the details used in the result.
*
* The quantile level is in decimal, i.e. 99% = 0.99 and 0 < level < 1 should be satisfied.
* This is measured from the bottom, that is, Thus the quantile estimation with the level 99% corresponds to
* the smallest 99% observations.
*
* The details consists on the indices of the samples actually used in the quantile computation - indices in the
* input sample - and the weights for each of those samples. The details are sufficient to recompute the
* quantile directly from the input sample.
*
* The sample observations are supposed to be unsorted, the first step is to sort the data.
*
* @param level the quantile level
* @param sample the sample observations
* @return The quantile estimation and its details
*/
public QuantileResult quantileDetailsFromUnsorted(double level, DoubleArray sample) {
return quantileDetails(level, sample, true, false);
}
/**
* Compute the expected shortfall and the details used in the result.
*
* The quantile level is in decimal, i.e. 99% = 0.99 and 0 < level < 1 should be satisfied.
* This is measured from the bottom, that is, Thus the expected shortfall with the level 99% corresponds to
* the smallest 99% observations.
*
* If index value computed from the level is outside of the sample data range, the nearest data point is used, i.e.,
* expected short fall is computed with flat extrapolation.
* Thus this is coherent to {@link #quantileWithExtrapolationFromUnsorted(double, DoubleArray)}.
*
* The details consists on the indices of the samples actually used in the expected shortfall computation - indices
* in the input sample - and the weights for each of those samples. The details are sufficient to recompute the
* expected shortfall directly from the input sample.
*
* The sample observations are supposed to be unsorted, the first step is to sort the data.
*
* @param level the quantile level
* @param sample the sample observations
* @return The expected shortfall estimation and its detail
*/
public QuantileResult expectedShortfallDetailsFromUnsorted(double level, DoubleArray sample) {
return quantileDetails(level, sample, true, true);
}
// Generic quantile computation with quantile details.
private QuantileResult quantileDetails(
double level,
DoubleArray sample,
boolean isExtrapolated,
boolean isEs) {
int nbData = sample.size();
double[] w = weights(nbData);
/* Sorting data and keeping weight information. The arrays are modified */
double[] s = sample.toArray();
DoubleArrayMath.sortPairs(s, w);
double[] s2 = sample.toArray();
double[] order = new double[s2.length];
for (int i = 0; i < s2.length; i++) {
order[i] = i;
}
DoubleArrayMath.sortPairs(s2, order);
/* Find the index. */
double runningWeight = 0.0d;
int index = nbData;
while (runningWeight < 1.0d - level) {
index--;
runningWeight += w[index];
}
if (isEs) {
return esFromIndexRunningWeight(index, runningWeight, s2, w, order, level);
}
return quantileFromIndexRunningWeight(index, runningWeight, isExtrapolated, s2, w, order, level);
}
/**
* Computes value-at-risk.
*
* @param index the index from which the VaR should be computed
* @param runningWeight the running weight up to index
* @param isExtrapolated flag indicating if value should be extrapolated (flat) beyond the last value
* @param s the sorted sample
* @param w the sorted weights
* @param order the order of the sorted sample in the unsorted sample
* @param level the level at which the VaR should be computed
* @return the VaR and the details of sample data used to compute it
*/
private QuantileResult quantileFromIndexRunningWeight(
int index,
double runningWeight,
boolean isExtrapolated,
double[] s,
double[] w,
double[] order,
double level) {
int nbData = s.length;
if ((index == nbData - 1) || (index == nbData)) {
ArgChecker.isTrue(isExtrapolated, "Quantile can not be computed above the highest probability level.");
return QuantileResult.of(s[nbData - 1], new int[]{(int) Math.round(order[nbData - 1])}, DoubleArray.of(1.0d));
}
double alpha = (runningWeight - (1.0 - level)) / w[index];
int[] indices = new int[nbData - index];
double[] impacts = new double[nbData - index];
for (int i = 0; i < nbData - index; i++) {
indices[i] = (int) Math.round(order[index + i]);
}
impacts[0] = 1 - alpha;
impacts[1] = alpha;
return QuantileResult.of((1 - alpha) * s[index] + alpha * s[index + 1], indices, DoubleArray.ofUnsafe(impacts));
}
/**
* Computes expected shortfall.
*
* @param index the index from which the ES should be computed
* @param runningWeight the running weight up to index
* @param s the sorted sample
* @param w the sorted weights
* @param order the order of the sorted sample in the unsorted sample
* @param level the level at which the ES should be computed
* @return the expected shortfall and the details of sample data used to compute it
*/
private QuantileResult esFromIndexRunningWeight(
int index,
double runningWeight,
double[] s,
double[] w,
double[] order,
double level) {
int nbData = s.length;
if ((index == nbData - 1) || (index == nbData)) {
return QuantileResult.of(s[nbData - 1], new int[]{(int) Math.round(order[nbData - 1])}, DoubleArray.of(1.0d));
}
double alpha = (runningWeight - (1.0 - level)) / w[index];
int[] indices = new int[nbData - index];
double[] impacts = new double[nbData - index];
for (int i = 0; i < nbData - index; i++) {
indices[i] = (int) Math.round(order[index + i]);
}
impacts[0] = 0.5 * (1 - alpha) * (1 - alpha) * w[index] / (1.0 - level);
impacts[1] = (alpha + 1) * 0.5 * (1 - alpha) * w[index] / (1.0 - level);
for (int i = 1; i < nbData - index - 1; i++) {
impacts[i] += 0.5 * w[index + i] / (1.0 - level);
impacts[i + 1] += 0.5 * w[index + i] / (1.0 - level);
}
impacts[nbData - index - 1] += w[nbData - 1] / (1.0 - level);
double es = 0;
for (int i = 0; i < nbData - index; i++) {
es += s[index + i] * impacts[i];
}
return QuantileResult.of(es, indices, DoubleArray.ofUnsafe(impacts));
}
@Override
protected QuantileResult quantile(double level, DoubleArray sortedSample, boolean isExtrapolated) {
throw new UnsupportedOperationException("Quantile available only from unsorted sample due to weights.");
}
@Override
protected QuantileResult expectedShortfall(double level, DoubleArray sortedSample) {
throw new UnsupportedOperationException("Expected Shortfall only from unsorted sample due to weights.");
}
/**
* Returns the weights for a given sample size.
*
* @param size the sample size
* @return the weights
*/
public double[] weights(int size) {
double w1 = (1.0 - 1.0D / lambda) / (1.0d - Math.pow(lambda, -size));
double[] w = new double[size];
for (int i = 0; i < size; i++) {
w[i] = w1 / Math.pow(lambda, i);
}
return w;
}
}