def from_configuration(cls, configuration: object, app_ns: str):
        '''
            See BaseStrategy.from_configuration for documentation
        '''

        analysis_date = util.get_business_date(
            constants.BUSINESS_DATE_DAYS_LOOKBACK, constants.BUSINESS_DATE_CUTOVER_TIME)

        try:
            config_params = dict(configuration.config[cls.CONFIG_SECTION])

            ticker_file_name = config_params['ticker_list_file_name']
            divergence_factor_threshold = float(
                config_params['divergence_factor_threshold'])
            macd_fast_period = int(config_params['macd_fast_period'])
            macd_slow_period = int(config_params['macd_slow_period'])
            macd_signal_period = int(config_params['macd_signal_period'])
        except Exception as e:
            raise ValidationError(
                "Could not read MACD Crossover Strategy configuration parameters", e)
        finally:
            configuration.close()

        ticker_list = TickerList.try_from_s3(app_ns, ticker_file_name)

        return cls(ticker_list, analysis_date, divergence_factor_threshold, macd_fast_period, macd_slow_period, macd_signal_period)
    def from_configuration(cls, configuration: object, app_ns: str):
        '''
            See BaseStrategy.from_configuration for documentation
        '''
        today = pd.to_datetime('today').date()

        try:
            config_params = dict(configuration.config[cls.CONFIG_SECTION])

            ticker_file_name = config_params['ticker_list_file_name']
            output_size = int(config_params['output_size'])
        except Exception as e:
            raise ValidationError(
                "Could not read MACD Crossover Strategy configuration parameters",
                e)

        ticker_list = TickerList.try_from_s3(app_ns, ticker_file_name)
        analysis_period = (pd.Period(today, 'M') - 1).strftime("%Y-%m")

        current_price_date = util.get_business_date(
            constants.BUSINESS_DATE_DAYS_LOOKBACK,
            constants.BUSINESS_DATE_CUTOVER_TIME)

        return cls(ticker_list, analysis_period, current_price_date,
                   output_size)
Ejemplo n.º 3
0
def get_service_inputs(app_ns: str):
    '''
        Returns the required inputs for the recommendation service given
        the application namespace used to identify the appropriate cloud resources.

        Returns
        ----------
        A tuple containing the latest SecurityRecommendationSet
        and Portfolio objects. If a portfolio does not exist, it will
        create a new one.
    '''

    log.info("Loading latest recommendations")
    recommendation_set = SecurityRecommendationSet.from_s3(
        app_ns, constants.S3_PRICE_DISPERSION_RECOMMENDATION_SET_OBJECT_NAME)

    business_date = util.get_business_date(
        constants.BUSINESS_DATE_DAYS_LOOKBACK,
        constants.BUSINESS_DATE_CUTOVER_TIME)

    if not recommendation_set.is_current(business_date):
        raise ValidationError("Current recommendation set is not valid", None)

    try:
        log.info("Loading current portfolio")
        pfolio = Portfolio.from_s3(app_ns, constants.S3_PORTFOLIO_OBJECT_NAME)
    except AWSError as e:
        if e.resource_not_found():
            pfolio = None
        else:
            raise e

    return (pfolio, recommendation_set)
    def __init__(self, ticker_list: list, analysis_period: str,
                 current_price_date: date, output_size: int):
        """
            Initializes the strategy given the ticker list, analysis period
            and output size.

            The period is used to determine the range of financial data required
            to perform the analysis, while the output size will limit the number
            of securities that are recommended by the strategy.

            Parameters
            ------------
            ticker_list : list of tickers to be included in the analisys
            analysis_period: The analysis period as a string. E.g. '2020-06'
            output_size : number of recommended securities that will be returned
                by this strategy
        """

        if ticker_list == None or len(ticker_list.ticker_symbols) < 2:
            raise ValidationError(
                "Ticker List must contain at least 2 symbols", None)

        if output_size == 0:
            raise ValidationError("Output size must be at least 1", None)

        try:
            self.analysis_period = pd.Period(analysis_period, 'M')
        except Exception as e:
            raise ValidationError("Could not parse Analysis Period", e)

        self.ticker_list = ticker_list

        self.output_size = output_size

        if (current_price_date == None):
            business_date = self.current_price_date = util.get_business_date(
                constants.BUSINESS_DATE_DAYS_LOOKBACK,
                constants.BUSINESS_DATE_CUTOVER_TIME)

            self.current_price_date = business_date
        else:
            self.current_price_date = current_price_date

        (self.analysis_start_date,
         self.analysis_end_date) = intrinio_util.get_month_period_range(
             self.analysis_period)

        if (self.analysis_start_date > self.current_price_date):
            raise ValidationError(
                "Price Date: [%s] must be greater than the Analysis Start Date [%s]"
                % (self.current_price_date, self.analysis_start_date), None)

        if (self.analysis_end_date > self.current_price_date):
            logging.debug("Setting analysis end date to 'today'")
            self.analysis_end_date = self.current_price_date

        self.recommendation_set = None
        self.raw_dataframe = None
        self.recommendation_dataframe = None
    def test_get_business_date_with_huge_days_offset(self):
        with patch.object(
                pd.Timestamp,
                'utcnow',
                return_value=pd.Timestamp('2020-06-10T23:00:00+0000')):

            self.assertEqual(util.get_business_date(100, time(17, 0, 0)),
                             date(2020, 9, 18))
    def test_get_business_date_after_market_close(self):
        with patch.object(
                pd.Timestamp,
                'utcnow',
                return_value=pd.Timestamp('2020-06-10T23:00:00+0000')):

            self.assertEqual(util.get_business_date(0, time(17, 0, 0)),
                             date(2020, 6, 10))
    def test_get_business_date_during_market_hours(self):
        with patch.object(
                pd.Timestamp,
                'utcnow',
                return_value=pd.Timestamp('2020-06-10T15:00:00+0000')):

            self.assertEqual(util.get_business_date(0, time(17, 0, 0)),
                             date(2020, 6, 9))
def main():
    """
        Main function for this script
    """
    try:
        app_ns = parse_params()

        log.info("Parameters:")
        log.info("Application Namespace: %s" % app_ns)

        business_date = util.get_business_date(
            constants.BUSINESS_DATE_DAYS_LOOKBACK, constants.BUSINESS_DATE_CUTOVER_TIME)
        log.info("Business Date is: %s" % business_date)

        # test all connectivity upfront, so if there any issues
        # the problem becomes more apparent
        connector_test.test_aws_connectivity()
        connector_test.test_intrinio_connectivity()

        log.info('Loading Strategy Configuration "%s" from S3' %
                 constants.STRATEGY_CONFIG_FILE_NAME)
        configuration = Configuration.try_from_s3(
            constants.STRATEGY_CONFIG_FILE_NAME, app_ns)

        log.info("Initalizing Trading Strategies")
        strategies = [
            PriceDispersionStrategy.from_configuration(configuration, app_ns),
            MACDCrossoverStrategy.from_configuration(configuration, app_ns)
        ]

        notification_list = []
        for strategy in strategies:
            recommendation_set = None
            try:
                log.info("Executing %s strategy" % strategy.STRATEGY_NAME)
                recommendation_set = SecurityRecommendationSet.from_s3(
                    app_ns, strategy.S3_RECOMMENDATION_SET_OBJECT_NAME)
            except AWSError as awe:
                if not awe.resource_not_found():
                    raise awe
                log.info("No recommendation set was found in S3.")

            if recommendation_set == None  \
                    or not recommendation_set.is_current(business_date):

                strategy.generate_recommendation()
                strategy.display_results()

                recommendation_set = strategy.recommendation_set

                recommendation_set.save_to_s3(
                    app_ns, strategy.S3_RECOMMENDATION_SET_OBJECT_NAME)

                notification_list.append(recommendation_set)
            else:
                log.info(
                    "Recommendation set is still valid. There is nothing to do")

        recommendation_svc.notify_new_recommendation(
            notification_list, app_ns)
    except Exception as e:
        stack_trace = traceback.format_exc()
        log.error("Could run script, because: %s" % (str(e)))
        log.error(stack_trace)