Esempio n. 1
0
 public void Deconstruct(out DoubleCurve <T> curve, out IReadOnlyList <Contract <T> > bootstrappedContracts)
 {
     curve = Curve;
     bootstrappedContracts = BootstrappedContracts;
 }
Esempio n. 2
0
 public override DoubleCurve <TIndex> Build()
 {
     return(DoubleCurve.FromDictionary(Data, _weighting));
 }
Esempio n. 3
0
 public BootstrapResults([NotNull] DoubleCurve <T> curve, [NotNull] List <Contract <T> > bootstrappedContracts)
 {
     Curve = curve ?? throw new ArgumentNullException(nameof(curve));
     BootstrappedContracts = bootstrappedContracts ?? throw new ArgumentNullException(nameof(bootstrappedContracts));
 }
Esempio n. 4
0
        // TODO include discount factors
        private static BootstrapResults <T> Calculate([NotNull] List <Contract <T> > contracts, [NotNull] Func <T, double> weighting,
                                                      [NotNull] List <Shaping <T> > shapings, bool allowRedundancy = false)
        {
            if (contracts == null)
            {
                throw new ArgumentNullException(nameof(contracts));
            }
            if (weighting == null)
            {
                throw new ArgumentNullException(nameof(weighting));
            }
            if (shapings == null)
            {
                throw new ArgumentNullException(nameof(shapings));
            }

            var contractsPlusShapingsCount = contracts.Count + shapings.Count;

            if (contractsPlusShapingsCount < 2)
            {
                throw new ArgumentException("contracts and shapings combined must contain at least two elements", nameof(contracts));
            }

            // TODO check if two contracts have the same Start and End?
            var minTimePeriod = contracts.Select(contract => contract.Start)
                                .Concat(shapings.Select(shaping => shaping.Start1))
                                .Concat(shapings.Select(shaping => shaping.Start2))
                                .Min(timePeriod => timePeriod);

            var maxTimePeriod = contracts.Select(contract => contract.End)
                                .Concat(shapings.Select(shaping => shaping.End1))
                                .Concat(shapings.Select(shaping => shaping.End2))
                                .Max(timePeriod => timePeriod);

            var numTimePeriods = maxTimePeriod.OffsetFrom(minTimePeriod) + 1;

            var matrix = Matrix <double> .Build.Dense(contractsPlusShapingsCount, numTimePeriods);

            var vector = Vector <double> .Build.Dense(contractsPlusShapingsCount);

            for (int i = 0; i < contracts.Count; i++)
            {
                var    contract    = contracts[i];
                double sumWeight   = 0.0;
                var    startOffset = contract.Start.OffsetFrom(minTimePeriod);
                var    endOffset   = contract.End.OffsetFrom(minTimePeriod);
                for (int j = startOffset; j <= endOffset; j++)
                {
                    var timePeriod = minTimePeriod.Offset(j);
                    var weight     = weighting(timePeriod);
                    matrix[i, j] = weight;
                    sumWeight   += weight;
                }
                if (sumWeight <= 0)
                {
                    throw new ArgumentException(
                              "sum of weighting evaluated to non-positive number for the following contract: " + contract);
                }
                vector[i] = contract.Price * sumWeight;
            }

            for (int i = 0; i < shapings.Count; i++)
            {
                var shaping      = shapings[i];
                int rowIndex     = i + contracts.Count;
                var startOffset1 = shaping.Start1.OffsetFrom(minTimePeriod);
                var endOffset1   = shaping.End1.OffsetFrom(minTimePeriod);

                var startOffset2 = shaping.Start2.OffsetFrom(minTimePeriod);
                var endOffset2   = shaping.End2.OffsetFrom(minTimePeriod);

                double sumWeighting1 = 0.0;
                for (int j = startOffset1; j <= endOffset1; j++)
                {
                    var timePeriod = minTimePeriod.Offset(j);
                    var weight     = weighting(timePeriod);
                    matrix[rowIndex, j] = weight;
                    sumWeighting1      += weight;
                }

                for (int j = startOffset1; j <= endOffset1; j++)
                {
                    matrix[rowIndex, j] /= sumWeighting1;
                }

                double sumWeighting2 = shaping.Start2.EnumerateTo(shaping.End2).Sum(weighting);
                // TODO refactor to eliminate duplicate evaluation of weighting

                if (shaping.ShapingType == ShapingType.Spread)
                {
                    // TODO refactor out common code to shared methods
                    for (int j = startOffset2; j <= endOffset2; j++)
                    {
                        var timePeriod = minTimePeriod.Offset(j);
                        var weight     = weighting(timePeriod);
                        matrix[rowIndex, j] -= weight / sumWeighting2;
                    }
                    vector[rowIndex] = shaping.Value;
                }
                else if (shaping.ShapingType == ShapingType.Ratio)
                {
                    // TODO refactor out common code to shared methods
                    for (int j = startOffset2; j <= endOffset2; j++)
                    {
                        var timePeriod = minTimePeriod.Offset(j);
                        var weight     = weighting(timePeriod);
                        matrix[rowIndex, j] += -weight * shaping.Value / sumWeighting2;
                    }
                    vector[rowIndex] = 0.0; // Not necessary, but just being explicit
                }
                else
                {
                    throw new InvalidEnumArgumentException($"shapings[{i}].ShapingType", (int)shaping.ShapingType,
                                                           typeof(ShapingType)); // TODO check the exception message and whether InvalidEnumArgumentException should be used in this context
                }
            }

            if (!allowRedundancy)
            {
                var matrixRank = matrix.Rank();
                if (matrixRank < matrix.RowCount)
                {
                    throw new ArgumentException("Redundant contracts and shapings are present");
                }
            }

            // Calculate least squares solution
            // TODO use alternative decomposition if full-rank http://www.imagingshop.com/linear-and-nonlinear-least-squares-with-math-net/
            // TODO does Math.Net offer a double tolerance parameter like NMath
            Vector <double> leastSquaresSolution = matrix.Svd(true).Solve(vector);

            var curvePeriods = new List <T>(leastSquaresSolution.Count);
            var curvePrices  = new List <double>(leastSquaresSolution.Count);

            // TODO pass raw results into BootstrapResults and have the processed result lazy calculated on access

            var bootstrappedContracts = new List <Contract <T> >();

            // TODO check if only one element?
            var allOutputPeriods = minTimePeriod.EnumerateTo(maxTimePeriod.Offset(1)).Skip(1).ToList();
            T   contractStart    = minTimePeriod;

            for (var i = 0; i < allOutputPeriods.Count; i++)
            {
                var outputPeriod    = allOutputPeriods[i];
                var inputStartsHere = contracts.Any(contract => contract.Start.Equals(outputPeriod)) ||
                                      shapings.Any(shaping => shaping.Start1.Equals(outputPeriod) || shaping.Start2.Equals(outputPeriod));

                var inputEndsOneStepBefore = contracts.Any(contract => contract.End.Next().Equals(outputPeriod)) ||
                                             shapings.Any(shaping => shaping.End1.Next().Equals(outputPeriod) || shaping.End2.Next().Equals(outputPeriod));

                // TODO refactor OR
                if (inputStartsHere || inputEndsOneStepBefore) // New output period
                {
                    var contractEnd = outputPeriod.Previous();

                    // Calculate weighted average price
                    // Add to bootstrappedContracts
                    double price = WeightedAveragePrice(contractStart, contractEnd, minTimePeriod, leastSquaresSolution, weighting);
                    bootstrappedContracts.Add(new Contract <T>(contractStart, contractEnd, price));

                    foreach (var period in contractStart.EnumerateTo(contractEnd))
                    {
                        curvePeriods.Add(period);
                        curvePrices.Add(price);
                    }

                    if (i < allOutputPeriods.Count - 1)
                    {
                        // Set contractStart unless last item of loop
                        if (!IsGapInContractsAndShapings(contracts, shapings, outputPeriod))
                        {
                            contractStart = outputPeriod;
                        }
                        else // outputPeriod is in a gap so need to increment i until not a gap
                        {
                            // TODO find more efficient way of doing this calculation
                            do
                            {
                                curvePeriods.Add(outputPeriod);
                                curvePrices.Add(price);
                                i++;
                                outputPeriod = outputPeriod.Next();
                            } while (IsGapInContractsAndShapings(contracts, shapings, outputPeriod));

                            contractStart = outputPeriod; // TODO remove top block of if as redundant?
                        }
                    }
                }
            }

            var curve = new DoubleCurve <T>(curvePeriods, curvePrices, weighting);

            return(new BootstrapResults <T>(curve, bootstrappedContracts));
        }