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 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)
    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_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())
示例#9
0
def main():
    """
        Main function of this script
    """
    try:
        #(app_ns, portfolio_size) = parse_params()
        (app_ns, portfolio_size) = ('sa', 3)

        log.info("Application Parameters")
        log.info("-app_namespace: %s" % app_ns)
        log.info("-portfolio_size: %d" % portfolio_size)

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

        (current_portfolio,
         security_recommendation) = portfolio_mgr_svc.get_service_inputs(app_ns)

        log.info("Loaded recommendation set id: %s" %
                 security_recommendation.model['set_id'])

        if current_portfolio is None:
            log.info("Creating new portfolio")
            current_portfolio = Portfolio(None)
            current_portfolio.create_empty_portfolio(security_recommendation)
        else:
            log.info("Repricing portfolio")
            current_portfolio.reprice(datetime.now())

        (updated_portfolio, updated) = portfolio_mgr_svc.update_portfolio(
            current_portfolio, security_recommendation, portfolio_size)

        # See if there is anything that needs to be traded
        market_open = td_ameritrade.equity_market_open(datetime.now())

        if market_open == True:
            broker = Broker()
            broker.cancel_all_open_orders()

            log.info("Market is open. Looking for trading opportunities")
            current_positions = td_ameritrade.positions_summary()

            try:
                if broker.reconcile_portfolio(current_positions, updated_portfolio) == False:
                    log.info(
                        "Portfolio is not in sync with brokerage account positions. Positions will be rebalanced")

                broker.materialize_portfolio(
                    current_positions, updated_portfolio)
            finally:
                updated_positions = td_ameritrade.positions_summary()
                broker.synchronize_portfolio(
                    updated_positions, updated_portfolio)

                updated_portfolio.recalc_returns()
                broker.cancel_all_open_orders()
        else:
            log.info("Market is closed. Nothing to trade")

        log.info("updated portfolio: %s" %
                 util.format_dict(updated_portfolio.to_dict()))

        log.info("Saving updated portfolio")
        updated_portfolio.save_to_s3(
            app_ns, constants.S3_PORTFOLIO_OBJECT_NAME)

        portfolio_mgr_svc.publish_current_returns(
            updated_portfolio, updated, app_ns)

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

        aws_service_wrapper.notify_error(e, "Portfolio Manager Service",
                                         stack_trace, app_ns)
def update_portfolio(current_portfolio: object, recommendation_set: object, portfolio_size: int):
    '''
        Updates the portolio based on the recommendation set.

        1) When a portfolio is empty, then create a new one
        2) When a portfolio is stale (based on an old recommendation), rebalance it.
        3) Else, do nothing.

        Returns
        -------
        A tuple containing the updated portfolio (copy) and a boolean flag indicating
        whether positions were updated (and should be traded)
    '''
    def select_random_portfolio(portfolio_size: int):
        '''
            selects a randmon subset of the security set and creates
            a portfolio out out it.
        '''
        security_set = updated_portfolio.model['securities_set']

        # portfolio size cannot be larger than the available securitues
        # in the recommendation
        if len(security_set) < portfolio_size:
            portfolio_size = len(security_set)

        if not 'current_portfolio' in updated_portfolio.model:
            updated_portfolio.model['current_portfolio'] = {}

        updated_portfolio.model['current_portfolio']['securities'] = []

        for _ in range(0, portfolio_size):
            random_security = random.choice(security_set)

            updated_portfolio.model['current_portfolio']['securities'].append({
                "ticker_symbol": random_security['ticker_symbol'],
                "quantity": 0,
                "purchase_date": None,
                "purchase_price": 0,
                "current_price": random_security['current_price'],
                "current_returns": 0,
                "trade_state": "UNFILLED",
                "order_id": None
            }
            )

            security_set.remove(random_security)

    if portfolio_size <= 0:
        raise ValidationError("Portfolio Size must be a positive number", None)

    updated_portfolio = current_portfolio.copy()

    updated = False
    pfolio_set_id = current_portfolio.model['set_id']
    rec_set_id = recommendation_set.model['set_id']

    if current_portfolio.is_empty():
        log.info("Portfolio is empty, selecting a new one")
        select_random_portfolio(portfolio_size)
        updated = True

    elif pfolio_set_id != rec_set_id:
        log.info("Recommendation set has changed, rebalancing portfolio")
        updated_portfolio = Portfolio()
        updated_portfolio.create_empty_portfolio(recommendation_set)
        select_random_portfolio(portfolio_size)
        updated = True
    else:
        log.info("Portfolio is still current. No rebalancing necessary")

    updated_portfolio.validate_model()

    return (updated_portfolio, updated)