Example #1
0
    def test_valid_dict(self):
        recommendation_set_dict = {
            "set_id":
            "1430b59a-5b79-11ea-8e96-acbc329ef75f",
            "creation_date":
            "2020-09-01T04:56:57.612693+00:00",
            "valid_from":
            "2019-08-01",
            "valid_to":
            "2019-08-31",
            "price_date":
            "2019-09-01",
            "strategy_name":
            "PRICE_DISPERSION",
            "security_type":
            "US Equities",
            "securities_set": [{
                "ticker_symbol": "GE",
                "price": 123.45
            }, {
                "ticker_symbol": "INTC",
                "price": 123.45
            }, {
                "ticker_symbol": "AAPL",
                "price": 123.45
            }]
        }

        SecurityRecommendationSet.from_dict(recommendation_set_dict)
Example #2
0
    def test_invalid_dict_2(self):
        recommendation_set_dict = {
            "set_id": "1430b59a-5b79-11ea-8e96-acbc329ef75f",
            "creation_date": "2020-09-01T04:56:57.612693+00:00",
            "valid_from": "2019-08-01",
            "price_date": "2019-09-01",
            "strategy_name": "PRICE_DISPERSION",
            "security_type": "US Equities"
        }

        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_dict(recommendation_set_dict)
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)

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

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

    return (pfolio, recommendation_set)
Example #4
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 test_update_portfolio_too_small(self):
        security_recommendation = SecurityRecommendationSet.from_dict(
            self.sr_dict)
        portfolio = Portfolio()
        portfolio.create_empty_portfolio(security_recommendation)

        with self.assertRaises(ValidationError):
            portfolio_mgr_svc.update_portfolio(
                portfolio, security_recommendation, 0)
    def test_create_empty_portfolio_valid(self):
        with patch.object(intrinio_data,
                          'get_latest_close_price',
                          return_value=('2019-08-31', 123.45)):

            recommendation_set = SecurityRecommendationSet.from_dict(
                self.sr_dict)

            portfolio = Portfolio()
            portfolio.create_empty_portfolio(recommendation_set)
    def generate_recommendation(self):
        """
            Applies the price dispersion algorithm and sets the following 
            instance variables:

            self.recommendation_set
                A SecurityRecommendationSet object with the current 
                recommendation
            self.raw_dataframe
                Dataframe with all stocks sorted into deciles. Useful for
                displaying intermediate results.
            self.recommendation_dataframe
                A Dataframe containing just the recommended stocks

            Returns
            ------------
            None
        """

        financial_data = self.__load_financial_data__()

        self.raw_dataframe = pd.DataFrame(financial_data)
        pd.options.display.float_format = '{:.3f}'.format

        # sort the dataframe into deciles
        self.raw_dataframe['decile'] = pd.qcut(
            financial_data['dispersion_stdev_pct'],
            10,
            labels=False,
            duplicates='drop')
        self.raw_dataframe = self.raw_dataframe.sort_values(
            ['decile', 'analyst_expected_return'], ascending=(False, False))

        self.recommendation_dataframe = self.raw_dataframe.head(
            self.output_size).drop([
                'decile', 'target_price_avg', 'dispersion_stdev_pct',
                'analyst_expected_return'
            ],
                                   axis=1)

        # price the recommended securitues
        priced_securities = {}
        for row in self.recommendation_dataframe.itertuples(index=False):
            priced_securities[row.ticker] = row.analysis_price

        # determine the recommendation valid date range
        valid = self.analysis_end_date + timedelta(days=1)

        (valid_from, valid_to) = intrinio_util.get_month_date_range(
            valid.year, valid.month)

        self.recommendation_set = SecurityRecommendationSet.from_parameters(
            datetime.now(), valid_from, valid_to, self.analysis_end_date,
            self.STRATEGY_NAME, "US Equities", priced_securities)
    def test_create_empty_portfolio_invalid_intrinio_response(self):
        with patch.object(intrinio_data,
                          'get_latest_close_price',
                          return_value=('aaaa', 123.45)):

            recommendation_set = SecurityRecommendationSet.from_dict(
                self.sr_dict)

            with self.assertRaises(ValidationError):
                portfolio = Portfolio()
                portfolio.create_empty_portfolio(recommendation_set)
    def test_create_empty_portfolio_no_prices(self):
        with patch.object(intrinio_data,
                          'get_latest_close_price',
                          side_effect=DataError("test exception", None)):

            recommendation_set = SecurityRecommendationSet.from_dict(
                self.sr_dict)

            with self.assertRaises(DataError):
                portfolio = Portfolio()
                portfolio.create_empty_portfolio(recommendation_set)
Example #10
0
    def test_is_current_current_date_single(self):
        # Create a recommendation set from the past (2019/8)
        recommendation_set = SecurityRecommendationSet.from_parameters(
            datetime(2020, 3, 1, 4, 56, 57, tzinfo=timezone.utc),
            date(2019, 8, 1), date(2019, 8, 1), date(2019, 8, 1),
            "PRICE_DISPERSION", "US Equities", {
                "GE": 123.45,
                "INTC": 123.45,
                "AAPL": 123.45
            })

        self.assertTrue(recommendation_set.is_current(date(2019, 8, 1)))
    def test_notify_new_recommendation_with_boto_error(self):
        with patch.object(aws_service_wrapper, 'cf_read_export_value',
                          return_value="some_sns_arn"), \
            patch.object(aws_service_wrapper, 'sns_publish_notification',
                         side_effect=AWSError("test exception", None)):

            notification_list = []
            notification_list.append(SecurityRecommendationSet.from_parameters(datetime.now(), datetime.now(
            ), datetime.now(), datetime.now(), 'STRATEGY_NAME', 'US Equities', {'AAPL': 100}))

            with self.assertRaises(AWSError):
                recommendation_svc.notify_new_recommendation(
                    notification_list, 'sa')
    def generate_recommendation(self):
        '''
            Analyzes all securitues supplied in the ticker list and returns a SecurityRecommendationSet
            object containing all stocks with a positive MACD crossover. These are stocks
            that are bullish and have positive momentum behind them.

            internally sets the self.recommendation_set object

        '''

        analysis_data = {
            'ticker_symbol': [],
            'price': [],
            'macd': [],
            'signal': [],
            'divergence': [],
            'momentum': []
        }

        recommended_securities = {}

        for ticker_symbol in self.ticker_list.ticker_symbols:
            (current_price, macd_line, signal_line) = self._read_price_metrics(
                ticker_symbol)

            buy_sell_indicator = self._analyze_security(
                current_price, macd_line, signal_line)

            analysis_data['ticker_symbol'].append(ticker_symbol)
            analysis_data['price'].append(current_price)
            analysis_data['macd'].append(macd_line)
            analysis_data['signal'].append(signal_line)
            analysis_data['divergence'].append(macd_line - signal_line)
            if buy_sell_indicator == True:
                analysis_data['momentum'].append("BULLISH")
                recommended_securities[ticker_symbol] = current_price
            else:
                analysis_data['momentum'].append("BEARISH")

        self.raw_dataframe = pd.DataFrame(analysis_data)
        self.raw_dataframe = self.raw_dataframe.sort_values(
            ['momentum', 'divergence'], ascending=(False, False))

        valid_from = valid_to = self.analysis_date

        self.recommendation_set = SecurityRecommendationSet.from_parameters(
            datetime.now(), valid_from, valid_to, self.analysis_date, self.STRATEGY_NAME,
            "US_EQUITIES", recommended_securities
        )
    def test_publish_current_returns(self):

        security_recommendation = SecurityRecommendationSet.from_dict(
            self.sr_dict)
        portfolio = Portfolio()
        portfolio.create_empty_portfolio(security_recommendation)
        (new_p, updated) = portfolio_mgr_svc.update_portfolio(
            portfolio, security_recommendation, 1)

        with patch.object(aws_service_wrapper, 'sns_publish_notification',
                          side_effect=AWSError("", None)), \
            patch.object(aws_service_wrapper, 'cf_read_export_value',
                         return_value="some_value"):

            with self.assertRaises(AWSError):
                portfolio_mgr_svc.publish_current_returns(new_p, updated, 'sa')
    def test_update_portfolio_too_big(self):
        security_recommendation = SecurityRecommendationSet.from_dict(
            self.sr_dict)
        portfolio = Portfolio()
        portfolio.create_empty_portfolio(security_recommendation)

        (new_p, updated) = portfolio_mgr_svc.update_portfolio(
            portfolio, security_recommendation, 100)

        '''
            ensure that portfolio contains all securitie from the recommendation set
        '''

        self.assertTrue(updated)
        self.assertEqual(len(new_p.model['current_portfolio'][
                         'securities']), len(security_recommendation.model['securities_set']))
        self.assertEqual(len(new_p.model['securities_set']), 0)
    def test_update_portfolio_empty_portfolio(self):
        security_recommendation = SecurityRecommendationSet.from_dict(
            self.sr_dict)
        portfolio = Portfolio()
        portfolio.create_empty_portfolio(security_recommendation)

        (new_p, updated) = portfolio_mgr_svc.update_portfolio(
            portfolio, security_recommendation, 1)

        '''
            ensure that
            1) portfolio is updated
            2) portfolio contains 1 security
            3) portfolio contains nothing in the security set
        '''

        self.assertTrue(updated)
        self.assertEqual(
            len(new_p.model['current_portfolio']['securities']), 1)
        self.assertEqual(len(new_p.model['securities_set']), len(
            security_recommendation.model['securities_set']) - 1)
    def test_update_portfolio_new_recommendation(self):

        sr_mod = deepcopy(self.sr_dict)
        sr_mod['set_id'] = 'different_set'

        security_recommendation = SecurityRecommendationSet.from_dict(sr_mod)
        portfolio = Portfolio.from_dict(self.portfolio_dict)

        (new_p, updated) = portfolio_mgr_svc.update_portfolio(
            portfolio, security_recommendation, 1)

        '''
            ensure that
            1) portfolio is updated
            2) set id are being set properly
        '''

        self.assertTrue(updated)
        self.assertEqual(new_p.model['set_id'],
                         security_recommendation.model['set_id'])
        self.assertNotEqual(new_p.model['set_id'], self.sr_dict['set_id'])
    def test_portfolio_not_empty(self):
        with patch.object(intrinio_data,
                          'get_latest_close_price',
                          return_value=('2019-08-31', 123.45)):

            recommendation_set = SecurityRecommendationSet.from_dict(
                self.sr_dict)

            portfolio = Portfolio()
            portfolio.create_empty_portfolio(recommendation_set)

            portfolio.model['current_portfolio'] = {
                'securities': [{
                    "ticker_symbol": "ABC",
                    "purchase_date": "2019-09-01T02:34:12.876012+00:00",
                    "purchase_price": 100,
                    "current_price": 200,
                    "trade_state": "FILLED",
                    "order_id": None
                }]
            }

            self.assertFalse(portfolio.is_empty())
Example #18
0
def main():
    """
        Main function for this script
    """
    try:
        (environment, ticker_file_name, output_size, month, year,
         current_price_date, app_ns) = parse_params()

        log.info("Parameters:")
        log.info("Environment: %s" % environment)
        log.info("Ticker File: %s" % ticker_file_name)
        log.info("Output Size: %d" % output_size)
        log.info("Analysis Month: %d" % month)
        log.info("Analysis Year: %d" % year)

        if environment == "TEST":
            log.info("reading ticker file from local filesystem")
            ticker_list = TickerFile.from_local_file(
                constants.TICKER_DATA_DIR, ticker_file_name).ticker_list

            log.info("Performing Recommendation Algorithm")
            strategy = PriceDispersionStrategy(ticker_list, year, month,
                                               output_size)
            strategy.generate_recommendation()
            display_calculation_dataframe(month, year, strategy,
                                          current_price_date)
        else:  # environment == "PRODUCTION"
            # 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("Reading ticker file from s3 bucket")
            ticker_list = TickerFile.from_s3_bucket(ticker_file_name,
                                                    app_ns).ticker_list

            log.info("Loading existing recommendation set from S3")
            recommendation_set = None

            try:
                recommendation_set = SecurityRecommendationSet.from_s3(app_ns)
            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(datetime.now()):

                log.info("Performing Recommendation Algorithm")
                strategy = PriceDispersionStrategy(ticker_list, year, month,
                                                   output_size)

                strategy.generate_recommendation()
                recommendation_set = strategy.recommendation_set
                display_calculation_dataframe(month, year, strategy,
                                              current_price_date)

                recommendation_set.save_to_s3(app_ns)
                recommendation_svc.notify_new_recommendation(
                    recommendation_set, app_ns)
            else:
                log.info(
                    "Recommendation set is still valid. There is nothing to do"
                )

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

        if environment == "PRODUCTION":
            aws_service_wrapper.notify_error(
                e, "Securities Recommendation Service", stack_trace, app_ns)
Example #19
0
 def test_valid_parameters(self):
     SecurityRecommendationSet.from_parameters(
         datetime.now(), datetime.now(), datetime.now(), datetime.now(),
         'STRATEGY_NAME', 'US Equities', {'AAPL': 100})
Example #20
0
    def test_invalid_parameters(self):
        '''
            Combine these into a single test for brevity
        '''

        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(None, date.today(),
                                                      date.today(),
                                                      datetime.now(),
                                                      'STRATEGY_NAME',
                                                      'US Equities',
                                                      {'AAPL': 100})
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(datetime.now(), None,
                                                      date.today(),
                                                      date.today(),
                                                      'STRATEGY_NAME',
                                                      'US Equities',
                                                      {'AAPL': 100})
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(datetime.now(),
                                                      date.today(), None,
                                                      datetime.now(),
                                                      'STRATEGY_NAME',
                                                      'US Equities',
                                                      {'AAPL': 100})
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(datetime.now(),
                                                      date.today(),
                                                      date.today(), None,
                                                      'STRATEGY_NAME',
                                                      'US Equities',
                                                      {'AAPL': 100})
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(datetime.now(),
                                                      date.today(),
                                                      date.today(),
                                                      date.today(), None,
                                                      'US Equities',
                                                      {'AAPL': 100})
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(datetime.now(),
                                                      date.today(),
                                                      date.today(),
                                                      date.today(),
                                                      'STRATEGY_NAME', None,
                                                      {'AAPL': 100})
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(datetime.now(),
                                                      date.today(),
                                                      date.today(),
                                                      date.today(),
                                                      'STRATEGY_NAME',
                                                      'US Equities', None)
        with self.assertRaises(ValidationError):
            SecurityRecommendationSet.from_parameters(
                datetime.now(), date.today(), date.today(), date.today(),
                'STRATEGY_NAME', 'US Equities', "Not A Dictionary")
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)
Example #22
0
 def test_invalid_dict_1(self):
     with self.assertRaises(ValidationError):
         SecurityRecommendationSet.from_dict({'x': 'y'})