def clear(self, startcash=0): ''' (Re-)Initialise the equity table in memory. ''' self.startcash = startcash self.cash = StockPrice([], []) self.stocks = StockPrice([], []) self.total = StockPrice([], [])
def load(self, system_id): ''' Read all equity of <system> from the database. ''' equity = Equity.objects.filter(system=system_id).order_by('date') dates = [] cash = [] stocks = [] totals = [] for row in equity: dates.append(row.date) cash.append(row.cash) stocks.append(row.positions) totals.append(row.cash + row.positions) self.cash = StockPrice(cash, dates) self.stocks = StockPrice(stocks, dates) self.total = StockPrice(totals, dates)
class EquityHistory(object): ''' This class maintains the history of equity. Equity is a DatedList of cash, stock and total value of the positions. These lists are kept in memory. The associated database table holds the equity position for each trading day. Total equity = cash + positions value on each date. ''' def __init__(self, startcash=None, system_id=None): if startcash: self.clear(startcash) if system_id: self.load(system_id) def clear(self, startcash=0): ''' (Re-)Initialise the equity table in memory. ''' self.startcash = startcash self.cash = StockPrice([], []) self.stocks = StockPrice([], []) self.total = StockPrice([], []) def percentage(self): ''' Return total equity, scaled so that the starting capital is 100. ''' factor = 100 / self.total[0] percentages = [t * factor for t in self.total] return StockPrice(percentages, self.total.dates) def update(self, date, cashflow, positions_value): ''' Store the equity position on <date> in the DatedLists. ''' try: lastcash = self.cash[-1] except IndexError: lastcash = self.startcash cash = lastcash + cashflow self.cash.append((date, cash)) self.stocks.append((date, positions_value)) self.total.append((date, cash + positions_value)) def exists(self): ''' Returns True if any equity exists in this instance, False otherwise. ''' return bool(self.total) def get(self, period='month'): ''' Returns a list of the equity position on each <period>. Where <period> may be one of the following: 'day', 'week', 'month' or 'year'. ''' data = zip(self.stocks.dates, self.stocks.as_list(), self.cash, self.total) start_equity = self.total[0] period_start = start_equity maxlen = len(self.stocks) - 1 equity = [] for i, (date, stocks, cash, total) in enumerate(data): total = float(total) if (period == 'day') or (i == maxlen) or (i == 0) or ( period == 'year' and date.year != self.stocks.dates[i+1].year) or ( period == 'month' and date.month != self.stocks.dates[i+1].month) or ( period == 'week' and date.isocalendar()[1] != self.stocks.dates[i+1].isocalendar()[1]): period_equity = { 'date': date, 'cash': cash, 'stocks': stocks, 'total': total, 'gain':100.*(total/start_equity-1)} if period != 'day': period_equity['period_gain'] = 100.*(total/period_start-1) equity.append(period_equity) period_start = total return equity def calc_results(self): ''' Returns a dict with: * max_dd: The maximum drawdown over a 1 year period. * min_dd_ratio: The lowest ratio of gain/max_dd in any one year period. * min_month: The lowest gain in any calender month (or highest loss) * max_month: The highest gain in any calender month (or lowest loss) * neg_month: the average number of negative months per year * sum_neg_months: the annualised sum of the losses during those months * ann_gain: annualised profit in % * min_year: The lowest gain in any 1 year period * max_year: The highest gain in any 1 year period ''' year = 252 # avg no of trading days in a year data = {'max_dd':0, 'min_dd_ratio': 999.99} year_gains = self.total.roc(year)[year-1:] if len(year_gains): data['min_year'] = min(year_gains) data['max_year'] = max(year_gains) else: data['min_year'] = 0 data['max_year'] = 0 year_drawdowns = self.total.drawdown(year) for year_dd, year_gain in zip(year_drawdowns, year_gains)[year-1:]: dd_ratio = div(year_gain, year_dd) # in %/% data['max_dd'] = max(data['max_dd'], year_dd) if dd_ratio is not None: data['min_dd_ratio'] = min(data['min_dd_ratio'], dd_ratio) total_gain = max(0, self.total[-1] / self.total[0]) dt = self.total.dates[-1] - self.total.dates[0] data['ann_profit'] = 100 * (total_gain ** (365./dt.days) - 1) months = self.get('month') data['min_month'] = min([x['period_gain'] for x in months]) data['max_month'] = max([x['period_gain'] for x in months]) neg_months = [x['period_gain'] for x in months if x['period_gain'] < 0] data['n_neg_month'] = 12. * len(neg_months) / len(months) data['sum_neg_mths'] = 12. * sum(neg_months) / len(months) # sanitise results: data['max_dd'] = min (data['max_dd'], 99) for key in ('ann_profit', 'sum_neg_mths', 'min_year', 'max_year', 'min_month', 'max_month', 'min_dd_ratio'): data[key] = min(max(data[key], -999), 999) return data def save(self, system): ''' Writes the equity history from the lists to the Equity database table. ''' with transaction.commit_on_success(): # for inserting in bulk (quicker) for date, cash, stocks in zip(self.cash.dates, self.cash, self.stocks): equity = {'system': system, 'date': date, 'cash':cash, 'positions': stocks} Equity.objects.create(**equity) def load(self, system_id): ''' Read all equity of <system> from the database. ''' equity = Equity.objects.filter(system=system_id).order_by('date') dates = [] cash = [] stocks = [] totals = [] for row in equity: dates.append(row.date) cash.append(row.cash) stocks.append(row.positions) totals.append(row.cash + row.positions) self.cash = StockPrice(cash, dates) self.stocks = StockPrice(stocks, dates) self.total = StockPrice(totals, dates) def write_thumbnail_to_db(self, system, width=100, height=32): ''' Calculate and store the y coordinates for generating a thumbnail image. ''' totals = self.total.as_list() Thumbnail.write_to_db(totals, system, width, height)