コード例 #1
0
    def test_synchronize_portfolio_all_extra_portfolio_positions(self):
        portfolio = Portfolio.from_dict(self.base_portfolio)

        broker = Broker()
        broker.synchronize_portfolio(self.base_positions, portfolio)

        portfolio.model['current_portfolio']['securities'].append({
            "ticker_symbol":
            "XXX",
            "quantity":
            1,
            "purchase_date":
            None,
            "purchase_price":
            1.0,
            "current_price":
            1.0,
            "current_returns":
            0.0,
            "trade_state":
            "UNFILLED",
            "order_id":
            None
        })

        self.assertEqual(
            portfolio.get_position('XXX')['trade_state'], 'UNFILLED')
コード例 #2
0
    def test_trade_buy_single_security(self):

        buy_positions = [('BA', 1.0)]

        portfolio = deepcopy(self.base_portfolio)
        del portfolio['current_portfolio']['securities'][2]
        del portfolio['current_portfolio']['securities'][1]

        portfolio = Portfolio.from_dict(portfolio)

        with patch.object(td_ameritrade, 'place_order', return_value='order-xxx'), \
                patch.object(td_ameritrade, 'list_recent_orders', return_value={
                    "order-xxx": {
                        "status": "FILLED",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    },
                }):

            broker = Broker()
            self.assertTrue(broker.trade('BUY', buy_positions, portfolio))

            pos = portfolio.get_position('BA')
            self.assertEqual(pos['trade_state'], 'FILLED')
            self.assertEqual(pos['quantity'], 1)
            self.assertIsNotNone(pos['purchase_date'])
コード例 #3
0
    def test_trade_buy_multiple_securities_with_exception(self):

        buy_positions = [('BA', 1.0), ('GE', 1.0), ('XOM', 1.0)]

        # portfolio has 3 positions
        portfolio = deepcopy(self.base_portfolio)

        portfolio = Portfolio.from_dict(portfolio)

        with patch.object(td_ameritrade, 'place_order', side_effect=[
                'order-1', 'order-2', TradeError("Some Error", None, None)]), \
                patch.object(td_ameritrade, 'list_recent_orders', return_value={
                    "order-1": {
                        "status": "FILLED",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    },
                    "order-2": {
                        "status": "FILLED",
                        "symbol": "GE",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    }
                }):

            broker = Broker()
            self.assertFalse(broker.trade('BUY', buy_positions, portfolio))
コード例 #4
0
    def test_generate_test_instructions_nothing_to_do(self):
        portfolio = Portfolio.from_dict(self.base_portfolio)
        broker = Broker()
        (sell_list, buy_list) = broker._generate_trade_instructions(
            self.base_positions, portfolio)

        self.assertListEqual(sell_list, [])
        self.assertListEqual(buy_list, [])
コード例 #5
0
    def test_generate_test_instructions_no_positions(self):
        portfolio = Portfolio.from_dict(self.base_portfolio)
        broker = Broker()
        (sell_list, buy_list) = broker._generate_trade_instructions({},
                                                                    portfolio)

        self.assertListEqual(sell_list, [])
        self.assertListEqual(buy_list, ['BA', 'GE', 'XOM'])
コード例 #6
0
    def test_trade_nothing_to_sell(self):
        '''
            Tests that when there is nothing to trade, 'trade' will still
            return true
        '''
        sell_positions = []

        broker = Broker()
        self.assertTrue(broker.trade('SELL', sell_positions, None))
コード例 #7
0
    def test_reconcile_portfolio_mismatching_quantity(self):
        pfolio = deepcopy(self.base_portfolio)

        pfolio['current_portfolio']['securities'][0]['quantity'] = 999

        portfolio = Portfolio.from_dict(pfolio)
        broker = Broker()
        self.assertFalse(
            broker.reconcile_portfolio(self.base_positions, portfolio))
コード例 #8
0
    def test_reconcile_portfolio_mismatching_ticker(self):
        pfolio = deepcopy(self.base_portfolio)
        pos = deepcopy(self.base_positions)

        pfolio['current_portfolio']['securities'][0]['ticker_symbol'] = 'XXX'

        with patch.object(td_ameritrade, 'login', return_value=None):
            portfolio = Portfolio.from_dict(pfolio)
            broker = Broker()
            self.assertFalse(broker.reconcile_portfolio(pos, portfolio))
コード例 #9
0
    def test_synchronize_portfolio_all_positions_filled(self):
        portfolio = Portfolio.from_dict(self.base_portfolio)
        broker = Broker()
        broker.synchronize_portfolio(self.base_positions, portfolio)

        for sec in portfolio.model['current_portfolio']['securities']:
            self.assertEqual(sec['trade_state'], 'FILLED')
            self.assertEqual(
                sec['purchase_price'], self.base_positions['equities'][
                    sec['ticker_symbol']]['averagePrice'])
コード例 #10
0
    def test_materialize_portfolio_cannot_sell(self):
        with patch.object(Broker, '_generate_trade_instructions', return_value=([('BA', 3)], [])), \
                patch.object(Broker, 'trade', return_value=False):

            broker = Broker()

            # Since I am patching "_generate_trade_instructions" these parameters
            # don't really matter
            with self.assertRaises(TradeError):
                broker.materialize_portfolio(self.base_positions,
                                             self.base_portfolio)
コード例 #11
0
    def test_reconcile_portfolio_fewer_positions(self):
        pfolio = deepcopy(self.base_portfolio)
        pos = deepcopy(self.base_positions)

        del pfolio['current_portfolio']['securities'][0]

        with patch.object(td_ameritrade, 'login', return_value=None):

            portfolio = Portfolio.from_dict(pfolio)
            broker = Broker()
            self.assertFalse(broker.reconcile_portfolio(pos, portfolio))
コード例 #12
0
    def test_reconcile_portfolio_matching(self):
        portfolio = deepcopy(self.base_portfolio)

        # this is the bare minimum required to reconcile the portfolio
        for sec in portfolio['current_portfolio']['securities']:
            sec['quantity'] = 1
            sec['trade_state'] = 'FILLED'

        portfolio = Portfolio.from_dict(portfolio)
        broker = Broker()
        self.assertTrue(
            broker.reconcile_portfolio(self.base_positions, portfolio))
コード例 #13
0
    def test_trade_buy_failed_trades(self):

        buy_positions = [('BA', 1.0), ('GE', 1.0), ('XOM', 1.0)]

        # portfolio has 3 positions
        portfolio = deepcopy(self.base_portfolio)

        portfolio = Portfolio.from_dict(portfolio)

        with patch.object(td_ameritrade, 'place_order', side_effect=[
                'order-1', 'order-2', 'order-3']), \
                patch.object(td_ameritrade, 'list_recent_orders', return_value={
                    "order-1": {
                        "status": "FILLED",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    },
                    "order-2": {
                        "status": "FILLED",
                        "symbol": "GE",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    },
                    "order-3": {
                        "status": "UNKNOWN",
                        "symbol": "XOM",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    },
                }):

            broker = Broker()
            self.assertTrue(broker.trade('BUY', buy_positions, portfolio))

            pos = portfolio.get_position('BA')
            self.assertEqual(pos['trade_state'], 'FILLED')
            self.assertEqual(pos['quantity'], 1)
            self.assertIsNotNone(pos['purchase_date'])

            pos = portfolio.get_position('GE')
            self.assertEqual(pos['trade_state'], 'FILLED')
            self.assertEqual(pos['quantity'], 1)
            self.assertIsNotNone(pos['purchase_date'])

            pos = portfolio.get_position('XOM')
            self.assertEqual(pos['trade_state'], 'UNFILLED')
            self.assertEqual(pos['quantity'], 0)
            self.assertIsNone(pos['purchase_date'])
コード例 #14
0
    def test_generate_test_instructions_with_buys(self):
        positions = deepcopy(self.base_positions)
        del positions['equities']['GE']
        del positions['equities']['XOM']

        portfolio = Portfolio.from_dict(self.base_portfolio)

        broker = Broker()
        (sell_list,
         buy_list) = broker._generate_trade_instructions(positions, portfolio)

        self.assertListEqual(sell_list, [])
        self.assertListEqual(buy_list, ['GE', 'XOM'])
コード例 #15
0
    def test_generate_test_instructions_with_sells(self):
        portfolio = deepcopy(self.base_portfolio)
        del portfolio['current_portfolio']['securities'][2]
        del portfolio['current_portfolio']['securities'][1]

        portfolio = Portfolio.from_dict(portfolio)

        broker = Broker()
        (sell_list, buy_list) = broker._generate_trade_instructions(
            self.base_positions, portfolio)

        self.assertListEqual(sell_list, [('GE', 1), ('XOM', 1)])
        self.assertListEqual(buy_list, [])
コード例 #16
0
    def test_synchronize_portfolio_no_boker_positions_filled(self):
        portfolio = Portfolio.from_dict(self.base_portfolio)
        broker = Broker()
        broker.synchronize_portfolio(
            {
                'equities': {},
                'cash': {
                    'cashAvailableForTrading': 100
                }
            }, portfolio)

        for sec in portfolio.model['current_portfolio']['securities']:
            self.assertEqual(sec['trade_state'], 'UNFILLED')
コード例 #17
0
    def test_trade_sell_single_security(self):

        sell_positions = [('BA', 1.0)]

        with patch.object(td_ameritrade, 'place_order', return_value='order-xxx'), \
                patch.object(td_ameritrade, 'list_recent_orders', return_value={
                    "order-xxx": {
                        "status": "FILLED",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser"
                    },
                }):

            broker = Broker()
            self.assertTrue(broker.trade('SELL', sell_positions, None))
コード例 #18
0
    def test_synchronize_portfolio_all_extra_broker_positions(self):
        portfolio = Portfolio.from_dict(self.base_portfolio)
        positions = deepcopy(self.base_positions)
        broker = Broker()
        broker.synchronize_portfolio(positions, portfolio)

        positions['equities']['XXX'] = {
            'longQuantity': 1.0,
            'averagePrice': 10.0,
            'marketValue': 10.0
        }

        for sec in portfolio.model['current_portfolio']['securities']:
            self.assertEqual(sec['trade_state'], 'FILLED')
            self.assertEqual(
                sec['purchase_price'],
                positions['equities'][sec['ticker_symbol']]['averagePrice'])
コード例 #19
0
    def test_cancel_all_open_orders_with_exception(self):
        '''
            Test that given a list of orders, some cancelable, some not,
            that only the cancelable ones are being considered
        '''
        with patch.object(td_ameritrade, 'cancel_order', side_effect=TradeError("SomeError", None, None)), \
                patch.object(td_ameritrade, 'list_recent_orders', return_value={
                    "order-1": {
                        "status": "FILLED",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser",
                        "cancelable": True
                    }
                }):

            broker = Broker()
            broker.cancel_all_open_orders()
コード例 #20
0
    def test_materialize_portfolio_no_cash_to_buy(self):
        '''
            Tests that when there is no cash to buy trades, no trading will
            be attempted
        '''
        with patch.object(Broker, '_generate_trade_instructions', return_value=([], ['BA', 'AAPL', 'MSFT'])), \
                patch.object(td_ameritrade, 'positions_summary', return_value={
                    "equities": {},
                    "cash": {
                        "cashAvailableForTrading": 1,
                    }
                }), \
                patch.object(td_ameritrade, 'get_latest_equity_price', return_value=100), \
                patch.object(Broker, 'trade', return_value=True) as mock_trade:

            broker = Broker()

            broker.materialize_portfolio(self.base_positions,
                                         self.base_portfolio)
            self.assertEqual(mock_trade.call_count, 0)
コード例 #21
0
    def test_cancel_all_open_orders_num_calls(self):
        '''
            Test that given a list of orders, some cancelable, some not,
            that only the cancelable ones are being considered
        '''
        with patch.object(td_ameritrade, 'cancel_order', return_value=None) as mock_cancel_order, \
                patch.object(td_ameritrade, 'list_recent_orders', return_value={
                    "order-1": {
                        "status": "FILLED",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": "2020-05-04T03:21:04+0000",
                        "tag": "AA_myuser",
                        "cancelable": False
                    },
                    "order-2": {
                        "status": "WORKING",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": None,
                        "tag": "AA_myuser",
                        "cancelable": True
                    },
                    "order-3": {
                        "status": "WORKING",
                        "symbol": "BA",
                        "quantity": 1,
                        "closeTime": None,
                        "tag": "AA_myuser",
                        "cancelable": True
                    },
                }):

            broker = Broker()
            broker.cancel_all_open_orders()

            self.assertEqual(mock_cancel_order.call_count, 2)
コード例 #22
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)