/** * 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 static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg; import static com.opengamma.strata.collect.TestHelper.assertThrowsWithCause; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; import org.testng.annotations.Test; import com.opengamma.strata.collect.DoubleArrayMath; import com.opengamma.strata.collect.array.DoubleArray; /** * Tests {@link ExponentiallyWeightedInterpolationQuantileMethod}. */ @Test public class ExponentiallyWeightedInterpolationQuantileMethodTest { private static final DoubleArray DATA_123 = DoubleArray.ofUnsafe(new double[] { 0.1746, 0.9716, 0.1963, 0.1982, 0.2020, 0.2155, 0.2222, 0.4534, 0.4690, 0.3717, 0.0286, 0.0363, 0.0379, 0.0582, 0.0611, 0.0622, 0.9368, 0.0776, 0.0779, 0.0822, 0.8053, 0.8075, 0.8190, 0.8190, 0.8216, 0.8234, 0.8399, 0.8421, 0.8557, 0.8914, 0.6243, 0.8631, 0.1544, 0.1674, 0.1740, 0.2401, 0.2413, 0.2582, 0.2666, 0.9620, 0.5807, 0.6278, 0.7532, 0.7543, 0.2936, 0.2979, 0.2998, 0.3000, 0.9524, 0.3028, 0.3057, 0.3076, 0.3461, 0.3508, 0.9157, 0.3781, 0.4011, 0.4157, 0.4197, 0.8814, 0.8870, 0.4285, 0.4463, 0.9077, 0.9150, 0.4481, 0.4863, 0.4878, 0.4908, 0.4942, 0.5029, 0.5083, 0.5108, 0.5212, 0.5224, 0.5290, 0.5578, 0.5780, 0.5803, 0.6325, 0.0849, 0.0916, 0.1055, 0.9275, 0.1358, 0.1474, 0.9731, 0.9600, 0.5921, 0.6174, 0.6343, 0.6416, 0.6423, 0.6694, 0.6748, 0.6763, 0.6793, 0.6804, 0.6859, 0.8483, 0.8511, 0.6460, 0.6495, 0.6504, 0.6570, 0.9570, 0.6666, 0.6862, 0.7136, 0.7145, 0.7289, 0.7291, 0.9813, 0.7360, 0.7444, 0.7602, 0.7714, 0.7768, 0.8024, 0.1841, 0.8963, 0.9198, 0.3734 }); private static final double LAMBDA = 0.995; private static final ExponentiallyWeightedInterpolationQuantileMethod METHOD = new ExponentiallyWeightedInterpolationQuantileMethod(LAMBDA); private static final double TOLERANCE_WEIGHT = 1.0E-6; private static final double TOLERANCE_QUANTILE = 1.0E-6; private static final double TOLERANCE_ES_NI = 1.0E-5; public void lambda_negative() { assertThrowsIllegalArg(() -> new ExponentiallyWeightedInterpolationQuantileMethod(-0.10d)); } public void lambda_zero() { assertThrowsIllegalArg(() -> new ExponentiallyWeightedInterpolationQuantileMethod(0.0d)); } public void lambda_above_1() { assertThrowsIllegalArg(() -> new ExponentiallyWeightedInterpolationQuantileMethod(1.10d)); } public void quantile_not_extrapolated() { double level = 0.999; assertThrowsIllegalArg(() -> METHOD.quantileFromUnsorted(level, DATA_123)); } public void quantile_from_sorted() { double level = 0.90; assertThrowsWithCause(() -> METHOD.quantileFromSorted(level, DATA_123), UnsupportedOperationException.class); } public void es_from_sorted() { double level = 0.90; assertThrowsWithCause(() -> METHOD.expectedShortfallFromSorted(level, DATA_123), UnsupportedOperationException.class); } public void quantile_last() { double level = 0.999; double qComputed = METHOD.quantileWithExtrapolationFromUnsorted(level, DATA_123); DoubleArray dataSorted = DATA_123.sorted(); assertEquals(qComputed, dataSorted.get(dataSorted.size()-1), TOLERANCE_WEIGHT, "Quantile."); } public void quantile() { double[] level = {0.98, 0.981, 0.9811, 0.97}; for(int i=0; i<level.length; i++){ check_quantile(level[i]); } } private void check_quantile(double level) { double[] w = METHOD.weights(DATA_123.size()); double qComputed = METHOD.quantileFromUnsorted(level, DATA_123); double WI1 = 0.0d; int nbW = 0; for (int i = 0; i < DATA_123.size(); i++) { if (DATA_123.get(i) > qComputed) { WI1 += w[i]; nbW++; } } assertTrue(WI1 < 1.0d - level, "Weight of tail lower than level"); double[] w2 = w.clone(); double[] data = DATA_123.toArray(); DoubleArrayMath.sortPairs(data, w2); double WI = WI1 + w2[w.length - 1 - nbW]; assertTrue(WI > 1.0d - level, "Weight of tail+1 larger than level"); double alpha = (WI - (1 - level)) / (WI - WI1); double qExpected = (1 - alpha) * data[w.length - 1 - nbW] + alpha * data[w.length - 1 - nbW + 1]; assertEquals(qComputed, qExpected, TOLERANCE_WEIGHT, "Quantile."); } public void weights() { double[] wComputed = METHOD.weights(DATA_123.size()); assertEquals(wComputed.length, DATA_123.size(), "Weight size is same as sample size"); double wTotal = 0.0d; for (int i = 0; i < wComputed.length; i++) { wTotal += wComputed[i]; } assertEquals(wTotal, 1.0, TOLERANCE_WEIGHT, "Total weight should be 1."); for (int i = 0; i < wComputed.length - 1; i++) { assertEquals(wComputed[i + 1], wComputed[i] / LAMBDA, TOLERANCE_WEIGHT, "Ratio between weights."); } } public void quantile_details() { double[] level = {0.98, 0.981, 0.9811, 0.97}; for (int i = 0; i < level.length; i++) { double q = METHOD.quantileFromUnsorted(level[i], DATA_123); QuantileResult r = METHOD.quantileDetailsFromUnsorted(level[i], DATA_123); assertEquals(r.getValue(), q, TOLERANCE_QUANTILE); assertEquals(r.getIndices().length, r.getWeights().size()); double qExpected = 0.0; for(int j=0; j<r.getIndices().length; j++){ // Recompute quantile from details qExpected += DATA_123.get(r.getIndices()[j]) * r.getWeights().get(j); } assertEquals(qExpected, q, TOLERANCE_QUANTILE); } } public void es_details() { double[] level = {0.98, 0.981, 0.9811, 0.97}; for (int i = 0; i < level.length; i++) { double es = METHOD.expectedShortfallFromUnsorted(level[i], DATA_123); QuantileResult r = METHOD.expectedShortfallDetailsFromUnsorted(level[i], DATA_123); assertEquals(r.getValue(), es, TOLERANCE_QUANTILE); assertEquals(r.getIndices().length, r.getWeights().size()); double qExpected = 0.0; for (int j = 0; j < r.getIndices().length; j++) { // Recompute ES from details qExpected += DATA_123.get(r.getIndices()[j]) * r.getWeights().get(j); } assertEquals(qExpected, es, TOLERANCE_QUANTILE); } } /* Compare Expected shortfall with numerical integral on the VaR. */ public void es() { double level = 0.95; double es = METHOD.expectedShortfallFromUnsorted(level, DATA_123); double q = METHOD.quantileFromUnsorted(level, DATA_123); assertTrue(es > q); int nbPts = 20; double esExpected = 0.0d; for (int i = 0; i < nbPts; i++) { double qIntegral = level + i / (nbPts - 1.0d) * (1 - level); esExpected += ((i == 0 || i == nbPts - 1) ? 0.5 : 1.0d) * METHOD.quantileWithExtrapolationFromUnsorted(qIntegral, DATA_123); // Trapezoid method } esExpected /= (nbPts - 1); assertEquals(es, esExpected, TOLERANCE_ES_NI); } public void es_extreme() { double level = 0.999; double es = METHOD.expectedShortfallFromUnsorted(level, DATA_123); double q = METHOD.quantileWithExtrapolationFromUnsorted(level, DATA_123); assertEquals(es, q, TOLERANCE_QUANTILE); } }