def get_stats_section(self, offset, count): """Creates a multi-line string with several columns of stats about the universe for a given time period. Args: offset: Number of rows (days) back to go when calculating stats. This must be at least 4 so that there are 2 periods to compare. count: Number of values to include for volatility and volume. """ period_end = self._daily['adj_close'].shape[0] period_start = period_end - offset period_midpoint = period_end - int(np.around(offset * .5)) # Prices with the most recent value at a max or min for the period. price_at_high_cols = self._daily['adj_close'].iloc[ period_start:, :].max(axis=0) == self._daily['adj_close'].iloc[ -1, :] price_at_high = 'At High\n' + text_utils.get_column( self._daily['adj_close'].ix[-1, price_at_high_cols], 2) price_at_low_cols = self._daily['adj_close'].iloc[ period_start:, :].min(axis=0) == self._daily['adj_close'].iloc[ -1, :] price_at_low = 'At Low\n' + text_utils.get_column( self._daily['adj_close'].ix[-1, price_at_low_cols], 2) # Change in price stdev from the first to second half of the period. # Ranges are inclusive because we care about differences across days. first_range = range(period_start, period_midpoint) second_range = range(period_midpoint - 1, period_end) volatility_change = self._daily['adj_close'].iloc[first_range, :].std( axis=0) / (self._daily['adj_close'].iloc[second_range, :].std( axis=0)) volatility_change = 'Volatility Chg\n' + text_utils.get_column( volatility_change.sort_values(ascending=False)[range(count)], 2, ( True)) # Change in volume from the first to second half of the period. # Ranges are not inclusive since we are summing volume for each day. first_range = range(period_start, period_midpoint) second_range = range(period_midpoint, period_end) volume_change = self._daily['volume'].iloc[first_range, :].sum( axis=0) / (self._daily['volume'].iloc[second_range, :].sum(axis=0)) volume_change = 'Volume Chg\n' + text_utils.get_column( volume_change.sort_values(ascending=False)[range(count)], 2, True) return text_utils.join_lines([price_at_high, price_at_low, ( volatility_change), volume_change], ' ')
def get_returns_section(self, offset, bins=None): """Creates a multi-line string containing a column of top winners and losers on the left, and a histogram of all returns on the right. Args: offset: Number of rows (days) back to go when calculating returns. bins: List of boundaries between histogram bins in ascending order. """ returns = ((self._daily['adj_close'].iloc[-1, :] - ( self._daily['adj_close'].iloc[-(offset + 1), :])) / ( self._daily['adj_close'].iloc[-(offset + 1), :])).sort_values() # If no bins provided, create default of 20 equally spaced bins. if bins is None: bins = np.arange(returns.min() - sys.float_info.epsilon, returns.max() + sys.float_info.epsilon, .05 * (returns.max() - returns.min())) else: # Possibly add bins at either end to ensure all values are counted. if returns.min() < bins.min(): bins = np.insert(bins, 0, returns.min() - ( sys.float_info.epsilon)) if returns.max() > bins.max(): bins = np.append(bins, returns.max() + sys.float_info.epsilon) returns_hist = text_utils.get_histogram(returns, bins, 0, True) # If there is space for winners and losers and at least one empty line # separating them, then include them. extreme_count = int(np.floor((bins.size - 1) * .5)) - 1 if extreme_count > 0: returns_col = text_utils.get_column(returns[:extreme_count], 1, ( True)) returns_col += ''.join('\n' * (bins.size - 2 * extreme_count - ( 1))) returns_col += text_utils.get_column(returns[-extreme_count:], 1, ( True)) else: returns_col = '' return text_utils.join_lines([returns_col, returns_hist], ' ')