2
\$\begingroup\$

I have an object which represents historical prices of certain financial instruments as below:

 class DailyPrice
        {
            public DateTime? Date { get; set; }
            public double? Price { get; set; }  
        }

This needs to be converted into QuarterlyPrice objects which will have all daily prices aggregated:

class QuarterlyPrice
        {
            public QuarterlyPrice()
            {
                DailyViews = new List<DailyPrice>();
            }
            public DateTime? Date { get; set; }
            public double? Price { get; set; }
            public List<DailyPrice> DailyViews { get; set; }
        }

Now I need to aggregate the daily prices into quarterly prices. I already have the logic of finding the quarters given a date range and the aggregation logic is as follows:

class Aggregater
        {
            public IEnumerable<QuarterlyPrice> GetQuarterlyAggViews(List<DailyPrice> dailyViews)
            {
                var dates = dailyViews.Where(x => x.Date.HasValue).Select(x => x.Date.Value).ToList();
                var dictionary = dailyViews.ToDictionary(x => x.Date.Value, x => x);

                //Will give me quarterly dates from the last to fist.
                var allQuarters = dates.Last().GetAllQuarters(dates.First());

                foreach (var quarter in allQuarters)
                {
                    var quarterlyPrice = new QuarterlyPrice();
                    var daily = dictionary.Where(x => x.Key <= quarter).Select(lx => lx.Value).ToList();
                    quarterlyPrice.DailyPrice.AddRange(daily);
                    quarterlyPrice.Date = quarter;
                    quarterlyPrice.Price = daily.Sum(x => x.Price);
                    yield return quarterlyPrice;
                }
            }
        }

My questions are the following:

  1. My daily prices can spawn 10-20 years and there will be as many daily price object. While aggregating them I'm putting them in the dictionary assuming the lookup would be quicker. Is this the correct thing to do? Is there a way to optimize the look up a bit further?

  2. How can I write a better aggregation logic for converting daily to quarterly price?

Note: The daily price list is sorted using .NET 4.0.

\$\endgroup\$
3
  • 4
    \$\begingroup\$ A GroupBy might be a better option. \$\endgroup\$ Jun 26, 2014 at 15:14
  • 7
    \$\begingroup\$ A dictionary only really makes sense if you are going to lookup items directly by key, you appear to be doing a range check so in my opinion you aren't gaining anything here. Also, as another side note, you should really be using decimal if Price is a monetary value. \$\endgroup\$
    – James
    Jun 26, 2014 at 15:15
  • \$\begingroup\$ you have to touch each price once anyway - so just follow the lists once. But I what I see here ... double for prices ... really? Why not some fixed-point number format? \$\endgroup\$
    – Random Dev
    Jun 26, 2014 at 15:17

3 Answers 3

2
\$\begingroup\$

What about a slightly different way of creating your classes?

class Quarter
{
    int Year { get; set; }
    List<DailyPrice> Prices { get; set; }
}
class DailyPrice
{
    public DailyPrice(DateTime? date, double? price)
    {
        Date = date;
        Price = price;
        QuarterPeriod = FindOrCreateQuarterObject(this);
    }
    public DateTime? Date { get; set; }
    public double? Price { get; set; }
    public Quarter QuarterPeriod { get; set; }
}

As you create your DailyPrice objects, you can add to the pointer list in the appropriate quarter. That way if you are collecting aggregate data by quarter all you have to do is spin through your quarters, and then do the aggregation on each quarter's list.

It takes a little more time upfront, and a few more pointers, but you'll save time if you plan to do multiple calculations.

\$\endgroup\$
0
1
\$\begingroup\$

Here is a clean way to do this using Linq. The code below will group by year and quarter, providing an IEnumerable of QuarterlyPrice.

// Test Data
List<DailyPrice> dailyPrices = new List<DailyPrice> {
    new DailyPrice { Date = new DateTime(2014, 01, 01), Price = 01},
    new DailyPrice { Date = new DateTime(2014, 02, 02), Price = 02},
    new DailyPrice { Date = new DateTime(2014, 03, 03), Price = 03},
    new DailyPrice { Date = new DateTime(2014, 04, 04), Price = 04},
    new DailyPrice { Date = new DateTime(2014, 05, 05), Price = 05},
    new DailyPrice { Date = new DateTime(2014, 06, 06), Price = 06},
    new DailyPrice { Date = new DateTime(2014, 07, 07), Price = 07},
    new DailyPrice { Date = new DateTime(2014, 08, 08), Price = 08},
    new DailyPrice { Date = new DateTime(2014, 09, 09), Price = 09},
    new DailyPrice { Date = new DateTime(2014, 10, 10), Price = 10},
    new DailyPrice { Date = new DateTime(2014, 11, 11), Price = 11},
    new DailyPrice { Date = new DateTime(2014, 12, 12), Price = 12},
};

// Group and Project
var results = dailyPrices.Where(d => d.Date.HasValue)
    .GroupBy(d => new { Year = d.Date.Value.Year, Quarter = ((d.Date.Value.Month - 1) / 3) })
    .Select(g => new QuarterlyPrice {
        DailyViews = g.ToList(),
        Date = new DateTime(g.Key.Year, (g.Key.Quarter * 3) + 1, 1),
        Price = g.Sum(d => d.Price)                
    });


// Results
foreach(var result in results)
    Console.WriteLine("{0:d}: {1:C}", result.Date, result.Price);
\$\endgroup\$
0
\$\begingroup\$

Isn't quarter a function of month and year, so you could do something like this:

var quarts = dailyViews.GroupBy(d => "" + (1+(d.Date.Value.Month-1) / 3) + "-" + d.Date.Value.Year)

This will give you a group where the Key is "Q-Year" and the items the list of prices.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.