Example #1
0
        public Func <double, double, double> GetMetric(bool isReverse)
        {
            return
                ((x, y) =>
            {
                y = Math.Abs(y) > Math.Abs(StretchY) ? Math.Sign(y) * Math.Abs(StretchY) : y;
                var offX = x + XOffset;
                var offC = XOffset != XShift / b ? XOffset : XShift;
                int sign;
                if (isReverse)
                {
                    sign = Math.Abs(offX % (-2 * Math.PI / StretchX)) < Math.PI / StretchX ? -1 : 1;
                }
                else
                {
                    sign = offX % (2 * Math.PI / StretchX) < Math.PI / StretchX ? 1 : -1;
                }

                var n = Math.Round(offX * StretchX / (2 * Math.PI));
                Func <double, double> func = u => StretchY *Math.Cos(StretchX *u + offC) + YShift;

                var bound = (sign * Math.Acos((y - YShift) / StretchY) - offC + 2 * Math.PI * n) / StretchX;
                var minimaX = x == bound ? x : FindMinimum.OfScalarFunctionConstrained(
                    u => (u - x) * (u - x) + (func(u) - y) * (func(u) - y),
                    Math.Min(x, bound), Math.Max(x, bound));
                return Math.Abs((minimaX - x) * (minimaX - x) + (func(minimaX) - y) * (func(minimaX) - y));
            });
        }
Example #2
0
        public static void TestGEV() // Scratchwork, prototyping, etc.
        {
            Logger output  = new Logger("GEV Test A.csv");
            Logger output2 = new Logger("GEV Test B.csv");
            //var dist = new ChiSquared(4, Program.rand);
            var dist = new Beta(2, 2);

            //var dist = new Beta(2, 5);
            //var dist = new Beta(2, 1.5);
            output.WriteLine($"Distribution: {dist.ToString().Replace(',',' ')}");
            //var dist = new Exponential(2, Program.rand);
            //var dist = new Gamma(2, 2, Program.rand);
            const int sampleSize = 300;

            output.WriteLine($"Samplesize: {sampleSize}");

            // Report the distribution 1-1/e quantile
            double upperQuantile = dist.InverseCumulativeDistribution(1 - 1.0 / sampleSize);
            double lowerQuantile = dist.InverseCumulativeDistribution(1.0 / sampleSize);

            output.WriteLine($"1-1/samplesize quantile: {upperQuantile}");
            output.WriteLine($"1/samplesize quantile: {lowerQuantile}");

            // Monte Carlo for the true distribution of the sample maximum
            double[] observations = new double[10000];

            for (int i = 0; i < observations.Length; i++)
            {
                double max = double.NegativeInfinity;
                for (int j = 0; j < sampleSize; j++)
                {
                    max = Math.Max(max, dist.Sample());
                }
                observations[i] = max;
            }
            Sorting.Sort(observations);

            ContinuousDistribution MonteCarloDistributionOfTheMaximum = ContinuousDistribution.ECDF(observations, Program.rand);

            // --- Find the best fit GEV distribution for this dataset ---

            #region Old code

            /*
             * // Compute location and scale parameter estimates for a given shape parameter Xi using the median and variance
             * void EstimateParameters(double shape, double median, double variance, out double location, out double scale)
             * {
             *  if (shape == 0)
             *  {
             *      scale = Math.Sqrt(6 * variance) / Math.PI;
             *      location = median + scale * Math.Log(Math.Log(2));
             *      return;
             *  }
             *  // This scale may or may not work for Xi > 0.5
             *  scale = Math.Sign(shape) * shape * Math.Sqrt(variance) / Math.Sqrt(SpecialFunctions.Gamma(1 - 2 * shape) - SpecialFunctions.Gamma(1 - shape) * SpecialFunctions.Gamma(1 - shape));
             *  if (double.IsNaN(scale)) scale = Math.Sqrt(6 * variance) / Math.PI;
             *  location = median - scale * (Math.Pow(Math.Log(2), -shape) - 1) / shape;
             * }*/
            #endregion

            double FitnessExactModel(GEV model)
            {
                double val = 0;

                for (int i = 0; i < observations.Length; i++)
                {
                    val += Math.Pow(model.CumulativeDistribution(observations[i]) - MonteCarloDistributionOfTheMaximum.CumulativeDensity(observations[i]), 2);
                }
                return(val);
            }

            #region Old code
            //double medianEst = Statistics.Median(observations);
            //double varianceEst = Statistics.VarianceEstimate(observations);

            /*
             * GEV Optimize(double startingval, out double fitness)
             * {
             *  double locationEst;
             *  double scaleEst;
             *  double bestScore = double.PositiveInfinity;
             *  GEV bestSoFar = null;
             *  bool increasing = false;
             *  int sinceImproved = 0;
             *  double shapeEst = startingval; // Neg or pos will stay that way throughout the optimization
             *
             *  while (true)
             *  {
             *      EstimateParameters(shapeEst, medianEst, varianceEst, out locationEst, out scaleEst);
             *      GEV model = new GEV(locationEst, scaleEst, shapeEst, Program.rand);
             *      double score = FitnessExactModel(model);
             *      if (score < bestScore)
             *      {
             *          bestScore = score;
             *          bestSoFar = model;
             *          sinceImproved = 0;
             *      }
             *      else
             *      {
             *          increasing ^= true;
             *          if (++sinceImproved > 10) break;
             *      }
             *      if (increasing) shapeEst += 0.3 * startingval;
             *      else shapeEst *= 0.5;
             *  }
             *  fitness = bestScore;
             *  return bestSoFar;
             * }
             *
             * GEV OptimizeV2(double initialGuess, out double fitness)
             * {
             *  double locationEst, scaleEst;
             *  double bestScore = double.PositiveInfinity;
             *  GEV bestSoFar = null;
             *  double shapeEst = initialGuess;
             *  double bestShapeSoFar = initialGuess;
             *  // Grow the estimate by doubling until it is no longer improving
             *  while (true)
             *  {
             *      EstimateParameters(shapeEst, medianEst, varianceEst, out locationEst, out scaleEst);
             *      GEV model = new GEV(locationEst, scaleEst, shapeEst, Program.rand);
             *      double score = FitnessExactModel(model);
             *      if (score < bestScore) // If it improved
             *      {
             *          bestScore = score;
             *          bestSoFar = model;
             *          bestShapeSoFar = shapeEst;
             *      }
             *      else break;
             *      shapeEst *= 2;
             *  }
             *  double magnitude = bestShapeSoFar;
             *  for (int i = 0; i < 10; i++) // 10 corresponds to 3 correct digits
             *  {
             *      double delta = magnitude * Math.Pow(2, -(i + 1)); // Half in size for each iteration
             *
             *      // Three positions: the current one, one lower by delta, and one higher by delta
             *
             *      // Lower Model
             *      EstimateParameters(bestShapeSoFar - delta, medianEst, varianceEst, out locationEst, out scaleEst);
             *      GEV lowerModel = new GEV(locationEst, scaleEst, bestShapeSoFar - delta, Program.rand);
             *      double lowerScore = FitnessExactModel(lowerModel);
             *
             *      // Upper Model
             *      EstimateParameters(bestShapeSoFar + delta, medianEst, varianceEst, out locationEst, out scaleEst);
             *      GEV upperModel = new GEV(locationEst, scaleEst, bestShapeSoFar + delta, Program.rand);
             *      double upperScore = FitnessExactModel(upperModel);
             *
             *      // Move to the best of the three
             *      double bestfitness = Math.Min(bestScore, Math.Min(upperScore, lowerScore));
             *      bestScore = bestfitness;
             *      if (lowerScore == bestfitness)
             *      {
             *          bestShapeSoFar = bestShapeSoFar - delta;
             *          bestSoFar = lowerModel;
             *      }
             *      else if (upperScore == bestfitness)
             *      {
             *          bestShapeSoFar = bestShapeSoFar + delta;
             *          bestSoFar = upperModel;
             *      }
             *  }
             *  fitness = bestScore;
             *  return bestSoFar;
             * }
             */
            #endregion

            GEV OptimizeBFGS(Func <Vector <double>, double> objectiveFunc, double initialShape, double initialScale, double initialLocation)
            {
                // Formatted by shape, scale, location
                var lowerBounds  = CreateVector.DenseOfArray(new double[] { -10, Math.Min(-3 * initialScale, 3 * initialScale), Math.Min(-3 * initialLocation, 3 * initialLocation) });
                var upperBounds  = CreateVector.DenseOfArray(new double[] { 10, Math.Max(-3 * initialScale, 3 * initialScale), Math.Max(-3 * initialLocation, 3 * initialLocation) });
                var initialGuess = CreateVector.DenseOfArray(new double[] { initialShape, initialScale, initialLocation });

                var min = FindMinimum.OfFunctionConstrained(objectiveFunc, lowerBounds, upperBounds, initialGuess);

                return(new GEV(min[2], min[1], min[0], Program.rand));
            }

            #region Old code

            // Optimize for Xi

            /*double fitNeg, fitZero, fitPos;
             * GEV bestNeg = OptimizeV2(-1, out fitNeg);
             * GEV bestPos = OptimizeV2(1, out fitPos);
             * double locZero, scaleZero;
             * EstimateParameters(0, medianEst, varianceEst, out locZero, out scaleZero);
             * GEV zeroModel = new GEV(locZero, scaleZero, 0, Program.rand);
             * fitZero = Fitness(zeroModel);
             * // Choose the best model of the three
             * double minScore = Math.Min(fitNeg, Math.Min(fitPos, fitZero));
             * GEV bestModel = null;
             * if (fitNeg == minScore) bestModel = bestNeg;
             * if (fitPos == minScore) bestModel = bestPos;
             * if (fitZero == minScore) bestModel = zeroModel; // Prefer zero, then pos
             *
             * Console.WriteLine($"Best Negative model: shape: {bestNeg.shape} scale: {bestNeg.scale} location: {bestNeg.location} fitness: {fitNeg}");
             * Console.WriteLine($"Best Positive model: shape: {bestPos.shape} scale: {bestPos.scale} location: {bestPos.location} fitness: {fitPos}");
             * Console.WriteLine($"Zero model: shape: {zeroModel.shape} scale: {zeroModel.scale} location: {zeroModel.location} fitness: {fitZero}");
             */
            #endregion

            double scaleGuess    = Math.Sqrt(6 * Statistics.VarianceEstimate(observations)) / Math.PI;
            double locationGuess = Statistics.Median(observations) + scaleGuess * Math.Log(Math.Log(2));
            double shapeGuess    = 0.5; // Use Pickands estimator here in the actual model
            Func <Vector <double>, double> objectiveFunction = x => FitnessExactModel(new GEV(x[2], x[1], x[0], Program.rand));
            GEV bestModelMonteCarlo = OptimizeBFGS(objectiveFunction, shapeGuess, scaleGuess, locationGuess);

            output.WriteLine($"MC Exact GEV Model: shape{bestModelMonteCarlo.shape} location{bestModelMonteCarlo.location} scale {bestModelMonteCarlo.scale}");

            double[] sample = new double[sampleSize];
            dist.Samples(sample); // Take a sample from dist
            Sorting.Sort(sample);
            // Report the sample min and max
            output.WriteLine($"Sample maximum: {sample[sample.Length - 1]}");
            //var sorter = new List<double>(sample);
            //sorter.Sort();
            //sample = sorter.ToArray();

            // Smoothed version
            //double[] smoothedData = new double[sample.Length - 1];
            //for (int i = 0; i < smoothedData.Length; i++) { smoothedData[i] = 0.5 * (sample[i] + sample[i + 1]); }
            //var pickandsApprox = new PickandsApproximation(smoothedData, method: PickandsApproximation.FittingMethod.Pickands_SupNorm); // Construct a Pickands tail approx from the sample

            var pickandsApprox = new GPDApproximation(sample, method: GPDApproximation.FittingMethod.V4); // Construct a Pickands tail approx from the sample
            // Bootstrap observations of the distribution of the sample maximum from the Pickands model
            double[] approxObservations = new double[observations.Length];
            for (int i = 0; i < approxObservations.Length; i++)
            {
                double max = double.NegativeInfinity;
                for (int j = 0; j < sampleSize; j++)
                {
                    max = Math.Max(max, pickandsApprox.Sample());
                }
                approxObservations[i] = max;
            }

            ContinuousDistribution approxECDF = ContinuousDistribution.ECDF(approxObservations); // ECDF of the bootstrapped observations
            //scaleGuess = Math.Sqrt(6 * Statistics.Variance(approxObservations)) / Math.PI;
            //locationGuess = Statistics.Median(approxObservations) + scaleGuess * Math.Log(Math.Log(2));
            // Guess location and scale
            shapeGuess = pickandsApprox.c;
            if (shapeGuess < 0)
            {
                double g1 = SpecialFunctions.Gamma(1 - shapeGuess);
                double g2 = SpecialFunctions.Gamma(1 - 2 * shapeGuess);
                scaleGuess    = Math.Sqrt(Statistics.Variance(approxObservations) * shapeGuess * shapeGuess / (g2 - g1 * g1));
                locationGuess = Statistics.Mean(approxObservations) - scaleGuess * (g1 - 1) / shapeGuess;
            }
            else
            {
                scaleGuess    = Math.Sqrt(6 * Statistics.Variance(approxObservations)) / Math.PI;
                locationGuess = Statistics.Median(approxObservations) + scaleGuess * Math.Log(Math.Log(2));
            }

            GEV estimatedGEVUnfitted = new GEV(location: locationGuess, scale: scaleGuess, shape: pickandsApprox.c); // Using the Pickands estimator for shape

            output.WriteLine($"UnfittedGEVModel: shape{estimatedGEVUnfitted.shape} location{estimatedGEVUnfitted.location} scale {estimatedGEVUnfitted.scale}");

            // Fit the model to the data drawn from the Pickands model
            double FitnessApproxModel(GEV model)
            {
                double val = 0;

                for (int i = 0; i < approxObservations.Length; i++)
                {
                    val += Math.Pow(model.CumulativeDistribution(approxObservations[i]) - approxECDF.CumulativeDensity(approxObservations[i]), 2);
                }
                return(val);
            }

            objectiveFunction = x => FitnessApproxModel(new GEV(x[2], x[1], x[0], Program.rand));
            GEV fittedApproxModel = OptimizeBFGS(objectiveFunction, pickandsApprox.c, scaleGuess, locationGuess);

            output.WriteLine($"FittedGEVModel: shape{fittedApproxModel.shape} location{fittedApproxModel.location} scale {fittedApproxModel.scale}");

            double[] proportions          = Interpolation.Linspace(0.000001, 0.999999, 2000);
            double[] observationQuantiles = Interpolation.Linspace(0.000001, 0.999999, 2000);
            for (int i = 0; i < observationQuantiles.Length; i++)
            {
                observationQuantiles[i] = Statistics.Quantile(observations, observationQuantiles[i]);
            }

            output.WriteLine("Abscissas,Monte Carlo ECDF,GEV Fit of MC ECDF,Estimated ECDF,Estimated GEV Unfitted,Estimated GEV Fitted,,ErrDistExactAbscissas,ErrDistExactValues,ErrDistModelAbscissas,ErrDistModelValues,ErrDistUnfittedAbscissas,ErrDistUnfittedValues");
            for (int i = 0; i < observationQuantiles.Length; i++)
            {
                output.WriteLine($"{observationQuantiles[i]}," +
                                 $"{MonteCarloDistributionOfTheMaximum.CumulativeDensity(observationQuantiles[i])}," +
                                 $"{bestModelMonteCarlo.CumulativeDistribution(observationQuantiles[i])}," +
                                 $"{approxECDF.CumulativeDensity(observationQuantiles[i])}," +
                                 $"{estimatedGEVUnfitted.CumulativeDistribution(observationQuantiles[i])}," +
                                 $"{fittedApproxModel.CumulativeDistribution(observationQuantiles[i])}," +
                                 $"," + // Space
                                 $"{observationQuantiles[i] - upperQuantile}," +
                                 $"{MonteCarloDistributionOfTheMaximum.CumulativeDensity(observationQuantiles[i])}," +
                                 //$"{quantiles[i] - sample[sample.Length - 1]}," +
                                 $"{estimatedGEVUnfitted.InverseCumulativeDistribution(proportions[i]) - estimatedGEVUnfitted.location}," +
                                 $"{proportions[i]}," +
                                 $"{fittedApproxModel.InverseCumulativeDistribution(proportions[i]) - fittedApproxModel.location}," +
                                 $"{proportions[i]}");
            }

            double[] distributionQuantiles = Interpolation.Linspace(0.000001, 0.999999, 2000);
            for (int i = 0; i < distributionQuantiles.Length; i++)
            {
                distributionQuantiles[i] = dist.InverseCumulativeDistribution(distributionQuantiles[i]);
            }
            output2.WriteLine("Abscissas,True CDF,Pickands Estimate");
            for (int i = 0; i < distributionQuantiles.Length; i++)
            {
                output2.WriteLine($"{distributionQuantiles[i]}," +
                                  $"{dist.CumulativeDistribution(distributionQuantiles[i])}," +
                                  $"{pickandsApprox.CDF(distributionQuantiles[i])}");
            }

            #region Temp for figure
            output2.WriteLine("");
            output2.WriteLine("TrueDist");
            output2.WriteLine("\\draw[line width=1.5pt]");

            for (int i = 0; i < distributionQuantiles.Length - 1; i++)
            {
                output2.WriteLine($"({distributionQuantiles[i]},{dist.CumulativeDistribution(distributionQuantiles[i])}) --");
            }
            output2.WriteLine($"({distributionQuantiles[distributionQuantiles.Length - 1]},{dist.CumulativeDistribution(distributionQuantiles[distributionQuantiles.Length - 1])});");


            output2.WriteLine("");
            output2.WriteLine("Approx");
            output2.WriteLine("\\draw[line width=1.5pt]");

            for (int i = 0; i < distributionQuantiles.Length; i++)
            {
                output2.WriteLine($"({distributionQuantiles[i]},{pickandsApprox.CDF(distributionQuantiles[i])}) --");
            }
            output2.WriteLine($"({distributionQuantiles[distributionQuantiles.Length - 1]},{pickandsApprox.CDF(distributionQuantiles[distributionQuantiles.Length - 1])});");

            #endregion

            // Clean up
            output.Dispose();
            output2.Dispose();
            //table.Dispose();
        }
Example #3
0
 /// <summary>
 /// Non-linear least-squares fitting the points (x,y) to an arbitrary function y : x -> f(p, x),
 /// returning its best fitting parameter p.
 /// https://github.com/mathnet/mathnet-numerics/issues/597
 /// http://kb.en-mat.com/Curve%20Fitting_%20Linear%20Regression.pdf
 /// </summary>
 public static double Curve(double[] x, double[] y, Func <double, double, double> f, double initialGuess, double tolerance = 1e-8, int maxIterations = 1000)
 {
     return(FindMinimum.OfScalarFunction(p => Distance.Euclidean(Generate.Map(x, t => f(p, t)), y), initialGuess, tolerance, maxIterations));
 }
Example #4
0
        public static Vector <double> Solve(
            string interpolationName,
            InitialTerm.InitialParameter param,
            double forward,
            double vtarget,
            List <double> strikeList)
        {
            InterpolationName = interpolationName;
            Param             = param;
            StrikeList        = strikeList;
            K25Call           = strikeList[3];
            KATM    = strikeList[2];
            K25Put  = strikeList[1];
            VolSS25 = param.volMS25;
            var tol         = 1.0E-5;
            var error       = 1.0;
            var LowerVector = Vector <double> .Build.DenseOfArray(new double[] { 0.00001, 0.00001, -0.99999 });

            var UpperVector = Vector <double> .Build.DenseOfArray(new double[] { 100, 100, 0.99999 });

            VolParameter = Vector <double> .Build.DenseOfArray(new double[] { 0.1, 0.1, -0.1 });

            while (Math.Abs(error) > tol)
            {
                if (InterpolationName == "SABR")
                {
                    VolParameter = FindMinimum.OfFunctionConstrained(
                        FuncOfVolCondtion,
                        LowerVector,
                        UpperVector,
                        VolParameter);
                    //VolParameter = FindMinimum.OfFunction(FuncOfVolCondtion, VolParameter);
                }
                else
                {
                    VolParameter = FindMinimum.OfFunction(FuncOfVolCondtion, VolParameter);
                }
                CalcStrikeList(VolParameter);
                var vTrial = CalcValueTrial(VolParameter);
                error = vTrial - vtarget;

                if (error > 0)
                {
                    VolSS25 -= tol / 100;
                }
                else
                {
                    VolSS25 += tol / 100;
                }
            }
            var volRR25 = VolCalculator(K25Call, VolParameter) - VolCalculator(K25Put, VolParameter);
            var Ans     = Vector <double> .Build.DenseOfArray(new double[]
            {
                VolParameter[0],
                VolParameter[1],
                VolParameter[2],
                K25Call,
                K25Put,
                VolSS25,
                volRR25
            });

            return(Ans);
        }
Example #5
0
 public static double Minimize(Func <double, double> fn, double x0, double tolerance = 1E-8)
 {
     return(FindMinimum.OfScalarFunction(fn, x0, tolerance));
 }
Example #6
0
        // Data must be sorted before using this method
        internal static void ApproximateExcessDistributionParametersBFGS(List <double> data, out double a, out double c, out double u)
        {
            double Fitness(Vector <double> input) // Input is assumed to be (a,c,u)
            {
                double sum = 0;
                //double weightsum = 0;

                // Compute the index of the next largest element that is at or after u in the data
                int nextLargestIndex = data.BinarySearch(input[2]);

                if (nextLargestIndex < 0)
                {
                    nextLargestIndex = ~nextLargestIndex + 1;
                }
                int knotCount = data.Count - nextLargestIndex;

                /* ECDF version MSE
                 * for (int i = 0; i < knotCount; i++)
                 * {
                 *  // The largest deviation should occur at one of the step points
                 *  double GHat = TailCDF(data[nextLargestIndex + i] - input[2], input[0], input[1]); // Args: x - u, a, c
                 *  double residual = i * 1.0 / knotCount - GHat; // Deviation from the top of the step at x_i
                 *  //double weight = knotCount - i + 1;
                 *  sum += residual * residual;
                 *  //sum += Math.Abs(residual) * weight;
                 *  //weightsum += weight;
                 * }
                 * return sum / knotCount; // Consider dividing by n or n^2 here
                 */
                //return sum / (weightsum * knotCount);

                // Smoothed version MSE
                for (int i = 0; i < knotCount - 1; i++)
                {
                    double GHat     = TailCDF(0.5 * (data[nextLargestIndex + i] + data[nextLargestIndex + i + 1]) - input[2], input[0], input[1]);
                    double residual = (2.0 * i + 3) / (2.0 * knotCount) - GHat;
                    sum += residual * residual;
                }
                //return sum / knotCount;
                return((1 + Math.Abs(input[1])) * sum / knotCount); // Weighted so that smaller magnitudes of c are preferred
            }

            // Get Pickands' estimates of a and c for m = n/16 + 1 as starting guesses, consistent with Z4M at the 75th percentile
            //double pickandsEstA, pickandsEstC;
            EstimateParams(data, data.Count / 16 + 1, out double pickandsEstC, out double pickandsEstA);
            double lowerBoundA = 0;
            double upperBoundA = 3 * pickandsEstA + 1;
            double lowerBoundC = Math.Min(3 * pickandsEstC, -3 * pickandsEstC) - 1;
            double upperBoundC = -lowerBoundC;
            // Initial guess for u is at data[(3/4)n]

            var optimum = FindMinimum.OfFunctionConstrained(Fitness,
                                                            lowerBound: CreateVector.DenseOfArray(new double[] { lowerBoundA, lowerBoundC, data[0] }),
                                                            upperBound: CreateVector.DenseOfArray(new double[] { upperBoundA, upperBoundC, data[data.Count - 3] }),
                                                            initialGuess: CreateVector.DenseOfArray(new double[] { pickandsEstA, pickandsEstC /*Math.Min(pickandsEstC, 0)*/, data[data.Count * 3 / 4] }));

            // Return parameters
            a = optimum[0];
            c = optimum[1];
            u = optimum[2];
        }