import bisect import collections from datetime import timedelta Update = collections.namedtuple("Update", ["timestamp", "value"]) class NotEnoughDataException(Exception): pass class TimeSeries: def __init__(self): self.series = [] def __getitem__(self, index): return self.series[index] def update(self, timestamp, value): bisect.insort_left(self.series, Update(timestamp, value)) def get_closing_price_list(self, on_date, num_days): closing_price_list = [] for i in range(num_days): chk = on_date.date() - timedelta(i) for price_event in reversed(self.series): if price_event.timestamp.date() > chk: pass if price_event.timestamp.date() == chk: closing_price_list.insert(0, price_event) break if price_event.timestamp.date() < chk: closing_price_list.insert(0, price_event) break return closing_price_list class MovingAverage: def __init__(self, series, timespan): self.series = series self.timespan = timespan def value_on(self, end_date): moving_avg_series = self.series.get_closing_price_list(end_date, self.timespan) if len(moving_avg_series) < self.timespan: raise NotEnoughDataException("Not enough data to calculate moving average") price_list = [update.value for update in moving_avg_series] return sum(price_list)/self.timespan