def __init__(self):
     self.db_model = DbModel()
     self.stock_prices = {}
 def __init__(self):
     self.db_model = DbModel()
class StockPriceProcessor(object):

    def __init__(self):
        self.db_model = DbModel()
        self.stock_prices = {}

    def set_stock_prices(self, company_id, from_date, price_type='adjclose'):
        """
        Set stock prices for days from given date to present day.
        Must be called before calling get_price_movement method.

        Args:
            company_id (int): Company ID
            from_date (Date): from which date to search (also some previous days will be saved)
        """
        self.stock_prices = {}
        # Substract some days to be sure to get data for the from_date.
        early_from_date = from_date - datetime.timedelta(days=7)
        # Get stock prices for individual dates.
        stock_prices = self.db_model.get_stock_prices(company_id, early_from_date, price_type)
        # Check if there was any result.
        if not stock_prices:
            return False
        # Create stock prices dictionary.
        for (price_date, price) in stock_prices:
            self.stock_prices[price_date] = price
        # OK
        return True

    def _get_price_movement(self, first_date, second_date):
        """
        Get relative stock price movement: R_t = (P_t - P_t-1) / P_t-1

        Args:
            first_date (Date)
            second_date (Date)

        Returns:
            float: Stock price movement between the two dates (as percentage change).
        """
        ratio = (self.stock_prices[second_date] - self.stock_prices[first_date]) / self.stock_prices[first_date]
        #print first_date, second_date, ratio * 100, self.stock_prices[first_date], self.stock_prices[second_date]
        return ratio * 100

    def get_price_movement_with_delay(self, document_date, days_delay, const_boundaries):
        """
        Calculate direction of price movement with given delay and constant boundaries.

        Args:
            document_date (Date): When was document published.
            days_delay (int): How many days from published day should be reaction date.
            const_boundaries (list): [-A, +A] maximal value for contant state.

        Returns:
            string: const, up, down
        """
        # Create delayed date.
        if days_delay > 0:
            reaction_date = document_date + datetime.timedelta(days=days_delay)
        else:
            reaction_date = document_date - datetime.timedelta(days=abs(days_delay))
        # Check if dates are present in DB.
        if reaction_date not in self.stock_prices or document_date not in self.stock_prices:
            return False
        # Calculate price movement.
        percentage_change = self._get_price_movement(document_date, reaction_date)
        #print percentage_change, const_boundaries, self._format_price_movement(percentage_change, const_boundaries)
        # Format movement to string.
        return self._format_price_movement(percentage_change, const_boundaries)

    def _format_price_movement(self, percentage_change, const_boundaries):
        """
        Get string representation of a size of stock movement.
        If the change is in interval (min, max), the movement is constant.
        If it's <= min and >= max, it's down and up, respectively.

        Args:
            percentage_change (float)
            const_boundaries (list): (min, max) interval for constant state

        Returns:
            string: const, up, down
        """
        if const_boundaries[0] < percentage_change < const_boundaries[1]:
            return 'const'
        if percentage_change > 0:
            return 'up'
        elif percentage_change < 0:
            return 'down'
        else:
            return 'const'
class StockPriceTransformer(object):
    def __init__(self):
        self.db_model = DbModel()

    def calculate_sma_for_company(self, company_id, period_length):
        """
        Calculate and save to DB simple moving average of adjclose company price.

        :param company_id (int)
        :param period_length (int): in days
        :return:
        """
        # Get daily closing prices.
        min_date = datetime.date(1900, 1, 1)
        close_prices = self.db_model.get_stock_prices(company_id, min_date, "adjclose")
        # For first N-1 days just insert the close price.
        values = []
        for s_date, s_price in close_prices[0 : period_length - 1]:
            values.append([company_id, s_date, s_price])
        # For remaining days calc the MA: (sum of current + previous n - 1 days) / period length
        for p_i, (s_date, s_price) in enumerate(close_prices[(period_length - 1) :], start=period_length - 1):
            # Debug info
            # print p_i, (close_prices[(p_i - period_length + 1)][1]), s_price,
            # Save values
            previous_days_sum = sum([x[1] for x in close_prices[(p_i - period_length + 1) : p_i]])
            total_sum = previous_days_sum + s_price
            mov_avg = total_sum / float(period_length)
            values.append([company_id, s_date, mov_avg])
        # Save values to DB.
        self.db_model.update_stock_prices_for_company("sma", values)

    def calculate_ewma_for_company(self, company_id, period_length):
        """
        Calculate and save to DB exponentially weighted moving average of adjclose company price.

        :param company_id:
        :param period_length:
        :return:
        """
        # Get daily closing prices.
        min_date = datetime.date(1900, 1, 1)
        close_prices = self.db_model.get_stock_prices(company_id, min_date, "adjclose")
        # Calculate EWMA for every day.
        ewma = pyma.NDayEMA(period_length)
        values = []
        for (s_date, s_price) in close_prices:
            a_value = ewma.compute(s_price)
            values.append([company_id, s_date, a_value])
        # Save values to DB.
        self.db_model.update_stock_prices_for_company("ewma", values)

    def calculate_ma_for_all_companies(self, avg_type, period_length):
        """
        Calculate and save to DB given moving average for all companies.

        :param avg_type: string: sma, ewma
        :param period_length: int
        :return:
        """
        print("Calculating %s for all companies.") % avg_type
        for company in self.db_model.get_companies():
            print("MA for company %d") % company[0]
            if avg_type == "sma":
                self.calculate_sma_for_company(company[0], period_length)
            elif avg_type == "ewma":
                self.calculate_ewma_for_company(company[0], period_length)
            else:
                raise ValueError("Unknown average type.")
        # OK
        print(">>>All company prices updated.")