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)); }); }
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(); }
/// <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)); }
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); }
public static double Minimize(Func <double, double> fn, double x0, double tolerance = 1E-8) { return(FindMinimum.OfScalarFunction(fn, x0, tolerance)); }
// 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]; }