public void Build_InputContractsInLinearTrend_ResultsLinear() { // Parameters of linear trend const double intercept = 45.7; const double dailySlope = 0.8; double dailyPrice = intercept; Day contractDay = new Day(2019, 5, 11); var contracts = new List <Contract <Hour> >(); for (int i = 0; i < 14; i++) { contracts.Add(Contract <Hour> .Create(contractDay, dailyPrice)); contractDay++; dailyPrice += dailySlope; } var curve = new MaxSmoothnessSplineCurveBuilder <Hour>() .AddContracts(contracts) .BuildCurve(); double hourlySlope = dailySlope / 24; foreach (var pricePair in curve.Data.Zip(curve.Data.Skip(1), (priceHour1, priceHour2) => new { priceHour1, priceHour2 })) { double hourlyChange = pricePair.priceHour2 - pricePair.priceHour1; Assert.AreEqual(hourlySlope, hourlyChange, 1E-12); } }
public void Build_WithOverlappingContracts_ThrowArgumentException() { var builder = new MaxSmoothnessSplineCurveBuilder <Month>() .AddContract(Month.CreateJanuary(2019), Month.CreateMarch(2019), 19.8) .AddContract(Month.CreateMarch(2019), 15.2) .AddContract(Month.CreateApril(2019), 10.02); Assert.Throws(Is.TypeOf <ArgumentException>().And.Message.EqualTo( "contracts are overlapping"), () => builder.BuildCurve()); }
public void Build_WithMonthTypeParameter_CurveConsistentWithInputs(List <Contract <Month> > contracts) { var results = new MaxSmoothnessSplineCurveBuilder <Month>() .AddContracts(contracts) .WithMultiplySeasonalAdjustment(MonthlySeasonalAdjust) // TODO add additive seasonal adjustment .WithFrontFirstDerivative(0.8) // TODO pass in as parameter? .WithBackFirstDerivative(-1.5) // TODO pass in as parameter? .BuildCurve(); TestHelper.AssertCurveConsistentWithInputs(contracts, period => period.End.Subtract(period.Start).Days, results, Tolerance); }
public void Build_InputContractsAllEqualAndNoSeasonalAdjustments_ResultsInFlatCurve() { const double flatPrice = 84.54; var curve = new MaxSmoothnessSplineCurveBuilder <Day>() .AddContract(Month.CreateJanuary(2019), flatPrice) .AddContract(Month.CreateFebruary(2019), flatPrice) .AddContract(Month.CreateMarch(2019), flatPrice) .AddContract(Quarter.CreateQuarter2(2019), flatPrice) .AddContract(Quarter.CreateQuarter3(2019), flatPrice) .AddContract(Season.CreateWinter(2019), flatPrice) .BuildCurve(); foreach (double price in curve.Data) { Assert.AreEqual(flatPrice, price, 1E-12); } }
static void Main(string[] args) { // The following code shows how to use the spline to derive a smooth daily curve from // monthly and quarterly granularity input contract prices. Also demonstrated is the // optional seasonal adjustment factor, in this case used to apply day-of-week seasonality. var dayOfWeekAdjustment = new Dictionary <DayOfWeek, double> { [DayOfWeek.Monday] = 0.95, [DayOfWeek.Tuesday] = 0.99, [DayOfWeek.Wednesday] = 1.05, [DayOfWeek.Thursday] = 1.01, [DayOfWeek.Friday] = 0.98, [DayOfWeek.Saturday] = 0.92, [DayOfWeek.Sunday] = 0.91 }; DoubleCurve <Day> curve = new MaxSmoothnessSplineCurveBuilder <Day>() .AddContract(Month.CreateJuly(2019), 77.98) .AddContract(Month.CreateAugust(2019), 76.01) .AddContract(Month.CreateSeptember(2019), 78.74) .AddContract(Quarter.CreateQuarter4(2019), 85.58) .AddContract(Quarter.CreateQuarter1(2020), 87.01) .WithMultiplySeasonalAdjustment(day => dayOfWeekAdjustment[day.DayOfWeek]) .BuildCurve(); Console.WriteLine(curve.FormatData("F5")); Console.WriteLine(); Console.WriteLine(); //=============================================================================================== // APPLYING AN ALTERNATIVE WEIGHTING SCHEME // By default, the spline calculations assume averages are weighted by the number of minutes in a contract period. // This assumption is fine for instruments where the commodity is delivered at a constant rate, e.g. natural gas forwards. // An alternative weighting scheme can be added by calling WithAverageWeighting and supplying a weighting scheme as a function. // The below example makes use of the Weighting helper class to provide the weighting function as the count of business days. // An example of when such a weighting scheme should be used is for oil swaps, based on an index which is only published on a business day // to create a monthly curve from quarterly granularity inputs. var holidays = new List <Day>() { new Day(2020, 1, 1) }; Func <Month, double> busDayWeight = Weighting.BusinessDayCount <Month>(holidays); var contracts = new List <Contract <Month> >() { Contract <Month> .Create(Quarter.CreateQuarter4(2019), 76.58), Contract <Month> .Create(Quarter.CreateQuarter1(2020), 77.20), Contract <Month> .Create(Quarter.CreateQuarter2(2020), 76.01), Contract <Month> .Create(Quarter.CreateQuarter3(2020), 74.95), Contract <Month> .Create(Quarter.CreateQuarter4(2020), 74.92), }; DoubleCurve <Month> curveBusDayWeight = new MaxSmoothnessSplineCurveBuilder <Month>() .AddContracts(contracts) .WithWeighting(busDayWeight) .BuildCurve(); Console.WriteLine("Derived smooth curve with business day weighting:"); Console.WriteLine(curveBusDayWeight.FormatData("F5", -1)); Console.ReadKey(); }