diff --git a/Makefile b/Makefile index 6407cac..4571b7a 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ test: - pytest + python -m pytest tests/ fix: autopep8 --in-place -r -a schwab diff --git a/schwab/client/base.py b/schwab/client/base.py index 737d752..b567f0d 100644 --- a/schwab/client/base.py +++ b/schwab/client/base.py @@ -171,16 +171,16 @@ def get_accounts(self, *, fields=None): ########################################################################## # Orders - def cancel_order(self, order_id, account_hash): - '''Cancel a specific order for a specific account''' - path = '/trader/v1/accounts/{}/orders/{}'.format(account_hash, order_id) - return self._delete_request(path) - def get_order(self, order_id, account_hash): '''Get a specific order for a specific account by its order ID''' path = '/trader/v1/accounts/{}/orders/{}'.format(account_hash, order_id) return self._get_request(path, {}) + def cancel_order(self, order_id, account_hash): + '''Cancel a specific order for a specific account''' + path = '/trader/v1/accounts/{}/orders/{}'.format(account_hash, order_id) + return self._delete_request(path) + class Order: class Status(Enum): '''Order statuses passed to :meth:`get_orders_for_account` and @@ -266,8 +266,7 @@ def get_orders_for_all_linked_accounts(self, max_results=None, from_entered_datetime=None, to_entered_datetime=None, - status=None, - statuses=None): + status=None): '''Orders for all linked accounts. Optionally specify a single status on which to filter. @@ -425,13 +424,13 @@ def get_user_preferences(self): '''Preferences for the logged in account, including all linked accounts.''' path = '/trader/v1/userPreference' - return self._get_request(path, ()) + return self._get_request(path, {}) ########################################################################## # Quotes - class GetQuote: + class Quote: class Fields(Enum): QUOTE = 'quote' FUNDAMENTAL = 'fundamental' @@ -447,9 +446,9 @@ def get_quote(self, symbol, *, fields=None): :param fields: Fields to request. If unset, return all available data. i.e. all fields. See :class:`GetQuote.Field` for options. ''' - fields = self.convert_enum_iterable(fields, self.GetQuote.Fields) + fields = self.convert_enum_iterable(fields, self.Quote.Fields) if fields: - params = {'fields': fields} + params = {'fields': ','.join(fields)} else: params = {} @@ -471,9 +470,9 @@ def get_quotes(self, symbols, *, fields=None, indicative=None): 'symbols': ','.join(symbols) } - fields = self.convert_enum_iterable(fields, self.GetQuote.Fields) + fields = self.convert_enum_iterable(fields, self.Quote.Fields) if fields: - params['fields'] = fields + params['fields'] = ','.join(fields) if indicative is not None: if type(indicative) is not bool: @@ -604,9 +603,9 @@ def get_option_chain( strike_range, self.Options.StrikeRange) option_type = self.convert_enum(option_type, self.Options.Type) exp_month = self.convert_enum(exp_month, self.Options.ExpirationMonth) + entitlement = self.convert_enum(entitlement, self.Options.Entitlement) params = { - 'apikey': self.api_key, 'symbol': symbol, } @@ -640,6 +639,8 @@ def get_option_chain( params['expMonth'] = exp_month if option_type is not None: params['optionType'] = option_type + if entitlement is not None: + params['entitlement'] = entitlement path = '/marketdata/v1/chains' return self._get_request(path, params) @@ -1010,7 +1011,7 @@ def get_movers(self, index, *, sort_order=None, frequency=None): if sort_order is not None: params['sort'] = sort_order if frequency is not None: - params['frequency'] = frequency + params['frequency'] = str(frequency) return self._get_request(path, params) @@ -1114,5 +1115,8 @@ def get_instrument_by_cusip(self, cusip): :param cusip: String representing CUSIP of instrument for which to fetch data. Note leading zeroes must be preserved. ''' + if not isinstance(cusip, str): + raise ValueError('cusip must be passed as str') + return self._get_request( '/marketdata/v1/instruments/{}'.format(cusip), {}) diff --git a/tests/client_test.py b/tests/client_test.py index 4b86a4c..161b4de 100644 --- a/tests/client_test.py +++ b/tests/client_test.py @@ -33,6 +33,7 @@ NOW_DATETIME = datetime.datetime(2020, 1, 2, 3, 4, 5) NOW_DATE = datetime.date(2020, 1, 2) NOW_DATETIME_ISO = '2020-01-02T03:04:05Z' +NOW_DATETIME_TRUNCATED_ISO = '2020-01-02T00:00:00Z' NOW_DATE_ISO = '2020-01-02' NOW_DATETIME_MINUS_60_DAYS = NOW_DATE - datetime.timedelta(days=60) @@ -95,164 +96,6 @@ def test_set_timeout(self): self.assertEqual(timeout, self.client.session.timeout) - ''' - # place_order - - - def test_place_order(self): - order_spec = {'order': 'spec'} - self.client.place_order(ACCOUNT_HASH, order_spec) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/orders'), json=order_spec) - - - def test_place_order_order_builder(self): - order_spec = OrderBuilder(enforce_enums=False).set_order_type('LIMIT') - expected_spec = {'orderType': 'LIMIT'} - self.client.place_order(ACCOUNT_HASH, order_spec) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/orders'), - json=expected_spec) - - - def test_place_order_str(self): - order_spec = {'order': 'spec'} - self.client.place_order(str(ACCOUNT_HASH), order_spec) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/orders'), json=order_spec) - - # replace_order - - - def test_replace_order(self): - order_spec = {'order': 'spec'} - self.client.replace_order(ACCOUNT_HASH, ORDER_ID, order_spec) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/orders/{orderId}'), - json=order_spec) - - - def test_replace_order_order_builder(self): - order_spec = OrderBuilder(enforce_enums=False).set_order_type('LIMIT') - expected_spec = {'orderType': 'LIMIT'} - self.client.replace_order(ACCOUNT_HASH, ORDER_ID, order_spec) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/orders/{orderId}'), - json=expected_spec) - - - def test_replace_order_str(self): - order_spec = {'order': 'spec'} - self.client.replace_order(str(ACCOUNT_HASH), str(ORDER_ID), order_spec) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/orders/{orderId}'), - json=order_spec) - - # create_saved_order - - - def test_create_saved_order(self): - order_spec = {'order': 'spec'} - self.client.create_saved_order(ACCOUNT_HASH, order_spec) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders'), - json=order_spec) - - - def test_create_saved_order_order_builder(self): - order_spec = OrderBuilder(enforce_enums=False).set_order_type('LIMIT') - expected_spec = {'orderType': 'LIMIT'} - self.client.create_saved_order(ACCOUNT_HASH, order_spec) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders'), - json=expected_spec) - - - def test_create_saved_order_str(self): - order_spec = {'order': 'spec'} - self.client.create_saved_order(str(ACCOUNT_HASH), order_spec) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders'), - json=order_spec) - - # delete_saved_order - - - def test_delete_saved_order(self): - self.client.delete_saved_order(ACCOUNT_HASH, SAVED_ORDER_ID) - self.mock_session.delete.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders/{savedOrderId}')) - - - def test_delete_saved_order_str(self): - self.client.delete_saved_order(str(ACCOUNT_HASH), str(SAVED_ORDER_ID)) - self.mock_session.delete.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders/{savedOrderId}')) - - # delete_saved_order - - - def test_get_saved_order(self): - self.client.get_saved_order(ACCOUNT_HASH, SAVED_ORDER_ID) - self.mock_session.get.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/savedorders/{savedOrderId}'), - params={}) - - - def test_get_saved_order_str(self): - self.client.get_saved_order(str(ACCOUNT_HASH), str(SAVED_ORDER_ID)) - self.mock_session.get.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/savedorders/{savedOrderId}'), - params={}) - - # get_saved_orders_by_path - - - def test_get_saved_orders_by_path(self): - self.client.get_saved_orders_by_path(ACCOUNT_HASH) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders'), params={}) - - - def test_get_saved_orders_by_path_str(self): - self.client.get_saved_orders_by_path(str(ACCOUNT_HASH)) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/savedorders'), params={}) - - # replace_saved_order - - - def test_replace_saved_order(self): - order_spec = {'order': 'spec'} - self.client.replace_saved_order(ACCOUNT_HASH, SAVED_ORDER_ID, order_spec) - self.mock_session.put.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/savedorders/{savedOrderId}'), - json=order_spec) - - - def test_replace_saved_order_order_builder(self): - order_spec = OrderBuilder(enforce_enums=False).set_order_type('LIMIT') - expected_spec = {'orderType': 'LIMIT'} - self.client.replace_saved_order(ACCOUNT_HASH, SAVED_ORDER_ID, order_spec) - self.mock_session.put.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/savedorders/{savedOrderId}'), - json=expected_spec) - - - def test_replace_saved_order_str(self): - order_spec = {'order': 'spec'} - self.client.replace_saved_order( - str(ACCOUNT_HASH), str(SAVED_ORDER_ID), order_spec) - self.mock_session.put.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/savedorders/{savedOrderId}'), - json=order_spec) - ''' - # get_account @@ -457,304 +300,516 @@ def test_get_orders_for_account_status_unchecked(self): }) - ''' - - - # get_orders_by_query + # get_orders_for_all_linked_accounts - @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_vanilla(self): - self.client.get_orders_by_query() + def test_get_orders_for_all_linked_accounts_vanilla(self): + self.client.get_orders_for_all_linked_accounts() self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, + self.make_url('/trader/v1/orders'), params={ + 'fromEnteredTime': NOW_DATETIME_MINUS_60_DAYS_ISO, 'toEnteredTime': NOW_DATETIME_ISO }) - + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_orders_for_all_linked_accounts_from_not_datetime(self): + with self.assertRaises(ValueError) as cm: + self.client.get_orders_for_all_linked_accounts( + from_entered_datetime='2020-01-02') + self.assertEqual( + str(cm.exception), + "expected type in (datetime.date, datetime.datetime) for " + + "from_entered_datetime, got 'builtins.str'") + + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_orders_for_all_linked_accounts_to_not_datetime(self): + with self.assertRaises(ValueError) as cm: + self.client.get_orders_for_all_linked_accounts( + to_entered_datetime='2020-01-02') + self.assertEqual( + str(cm.exception), + "expected type in (datetime.date, datetime.datetime) for " + + "to_entered_datetime, got 'builtins.str'") + + @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_max_results(self): - self.client.get_orders_by_query(max_results=100) + def test_get_orders_for_all_linked_accounts_max_results(self): + self.client.get_orders_for_all_linked_accounts(max_results=100) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, + self.make_url('/trader/v1/orders'), params={ + 'fromEnteredTime': NOW_DATETIME_MINUS_60_DAYS_ISO, 'toEnteredTime': NOW_DATETIME_ISO, 'maxResults': 100, }) - + @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_from_entered_datetime(self): - self.client.get_orders_by_query(from_entered_datetime=EARLIER_DATETIME) + def test_get_orders_for_all_linked_accounts_from_entered_datetime(self): + self.client.get_orders_for_all_linked_accounts( + from_entered_datetime=datetime.datetime( + year=2024, month=6, day=5, hour=4, minute=3, second=2)) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': EARLIER_ISO, + self.make_url('/trader/v1/orders'), params={ + 'fromEnteredTime': '2024-06-05T04:03:02Z', 'toEnteredTime': NOW_DATETIME_ISO, }) - + @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_to_entered_datetime(self): - self.client.get_orders_by_query(to_entered_datetime=EARLIER_DATETIME) + def test_get_orders_for_all_linked_accounts_to_entered_datetime(self): + self.client.get_orders_for_all_linked_accounts( + to_entered_datetime=datetime.datetime( + year=2024, month=6, day=5, hour=4, minute=3, second=2)) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, - 'toEnteredTime': EARLIER_ISO, + self.make_url('/trader/v1/orders'), params={ + 'fromEnteredTime': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'toEnteredTime': '2024-06-05T04:03:02Z', }) - - @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_status_and_statuses(self): - with self.assertRaises( - ValueError, msg='at most one of status or statuses may be set'): - self.client.get_orders_by_query( - to_entered_datetime=EARLIER_DATETIME, - status='EXPIRED', statuses=[ - self.client_class.Order.Status.FILLED, - self.client_class.Order.Status.EXPIRED]) - @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_status(self): - self.client.get_orders_by_query(status=self.client_class.Order.Status.FILLED) + def test_get_orders_for_all_linked_accounts_status(self): + self.client.get_orders_for_all_linked_accounts( + status=self.client_class.Order.Status.FILLED) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, + self.make_url('/trader/v1/orders'), params={ + 'fromEnteredTime': NOW_DATETIME_MINUS_60_DAYS_ISO, 'toEnteredTime': NOW_DATETIME_ISO, 'status': 'FILLED' }) - + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_orders_for_all_linked_accounts_multiple_statuses(self): + with self.assertRaises(ValueError) as cm: + self.client.get_orders_for_all_linked_accounts( + status=[self.client_class.Order.Status.FILLED, + self.client_class.Order.Status.REJECTED]) + self.assertIn( + 'expected type "Status", got type "list"', + str(cm.exception)) + + @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_status_unchecked(self): + def test_get_orders_for_all_linked_accounts_status_unchecked(self): self.client.set_enforce_enums(False) - self.client.get_orders_by_query(status='FILLED') + self.client.get_orders_for_all_linked_accounts(status='NOT_A_STATUS') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, + self.make_url('/trader/v1/orders'), params={ + 'fromEnteredTime': NOW_DATETIME_MINUS_60_DAYS_ISO, 'toEnteredTime': NOW_DATETIME_ISO, - 'status': 'FILLED' + 'status': 'NOT_A_STATUS' }) + + # place_order + + + def test_place_order(self): + order_spec = {'order': 'spec'} + self.client.place_order(ACCOUNT_HASH, order_spec) + self.mock_session.post.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/orders'), json=order_spec) + + + def test_place_order_order_builder(self): + order_spec = OrderBuilder(enforce_enums=False).set_order_type('LIMIT') + expected_spec = {'orderType': 'LIMIT'} + self.client.place_order(ACCOUNT_HASH, order_spec) + self.mock_session.post.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/orders'), + json=expected_spec) + + + def test_place_order_str(self): + order_spec = {'order': 'spec'} + self.client.place_order(str(ACCOUNT_HASH), order_spec) + self.mock_session.post.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/orders'), json=order_spec) + + # replace_order + + + def test_replace_order(self): + order_spec = {'order': 'spec'} + self.client.replace_order(ACCOUNT_HASH, ORDER_ID, order_spec) + self.mock_session.put.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/orders/{orderId}'), + json=order_spec) + + + def test_replace_order_order_builder(self): + order_spec = OrderBuilder(enforce_enums=False).set_order_type('LIMIT') + expected_spec = {'orderType': 'LIMIT'} + self.client.replace_order(ACCOUNT_HASH, ORDER_ID, order_spec) + self.mock_session.put.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/orders/{orderId}'), + json=expected_spec) + + + def test_replace_order_str(self): + order_spec = {'order': 'spec'} + self.client.replace_order(str(ACCOUNT_HASH), str(ORDER_ID), order_spec) + self.mock_session.put.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/orders/{orderId}'), + json=order_spec) + + + # get_transactions + @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_statuses(self): - self.client.get_orders_by_query(statuses=[ - self.client_class.Order.Status.FILLED, - self.client_class.Order.Status.EXPIRED]) + def test_get_transactions(self): + self.client.get_transactions(ACCOUNT_HASH) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, - 'toEnteredTime': NOW_DATETIME_ISO, - 'status': 'FILLED,EXPIRED' - }) + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': ','.join(t.value for t in self.client.Transactions.TransactionType), + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': NOW_DATETIME_ISO}) + - @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_statuses_scalar(self): - self.client.get_orders_by_query(statuses=self.client_class.Order.Status.FILLED) + def test_get_transactions_one_type(self): + self.client.get_transactions( + ACCOUNT_HASH, + transaction_types=self.client.Transactions.TransactionType.TRADE) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, - 'toEnteredTime': NOW_DATETIME_ISO, - 'status': 'FILLED' - }) + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': 'TRADE', + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': NOW_DATETIME_ISO}) + + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_transactions_type_list(self): + self.client.get_transactions( + ACCOUNT_HASH, + transaction_types=[ + self.client.Transactions.TransactionType.TRADE, + self.client.Transactions.TransactionType.JOURNAL]) + self.mock_session.get.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': 'TRADE,JOURNAL', + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': NOW_DATETIME_ISO}) + - @patch('schwab.client.base.datetime.datetime', mockdatetime) - def test_get_orders_by_query_statuses_unchecked(self): + def test_get_transactions_type_list_unchecked(self): self.client.set_enforce_enums(False) - self.client.get_orders_by_query(statuses=['FILLED', 'EXPIRED']) + self.client.get_transactions( + ACCOUNT_HASH, transaction_types=['TRADE', 'JOURNAL']) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/orders'), params={ - 'fromEnteredTime': MIN_ISO, - 'toEnteredTime': NOW_DATETIME_ISO, - 'status': 'FILLED,EXPIRED' - }) + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': 'TRADE,JOURNAL', + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': NOW_DATETIME_ISO}) - ''' - ''' - # search_instruments + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_transactions_symbol(self): + self.client.get_transactions(ACCOUNT_HASH, symbol='AAPL') + self.mock_session.get.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': ','.join(t.value for t in self.client.Transactions.TransactionType), + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': NOW_DATETIME_ISO, + 'symbol': 'AAPL'}) - - def test_search_instruments(self): - self.client.search_instruments( - ['AAPL', 'MSFT'], self.client_class.Instrument.Projection.FUNDAMENTAL) + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_transactions_symbol_start_date_as_datetime(self): + self.client.get_transactions( + ACCOUNT_HASH, start_date=NOW_DATETIME) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/instruments'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL,MSFT', - 'projection': 'fundamental'}) + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': ','.join(t.value for t in self.client.Transactions.TransactionType), + 'startDate': NOW_DATETIME_ISO, + 'endDate': NOW_DATETIME_ISO}) - - def test_search_instruments_one_instrument(self): - self.client.search_instruments( - 'AAPL', self.client_class.Instrument.Projection.FUNDAMENTAL) + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_transactions_symbol_start_date_as_date(self): + self.client.get_transactions( + ACCOUNT_HASH, start_date=NOW_DATE) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/instruments'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'projection': 'fundamental'}) + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': ','.join(t.value for t in self.client.Transactions.TransactionType), + 'startDate': NOW_DATETIME_TRUNCATED_ISO, + 'endDate': NOW_DATETIME_ISO}) + + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_transactions_symbol_end_date_as_datetime(self): + self.client.get_transactions( + ACCOUNT_HASH, + # NOW_DATETIME is the default, use something different + end_date=datetime.datetime(2020, 6, 7, 8, 9, 0)) + self.mock_session.get.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': ','.join(t.value for t in self.client.Transactions.TransactionType), + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': '2020-06-07T08:09:00Z'}) + + @patch('schwab.client.base.datetime.datetime', mockdatetime) + def test_get_transactions_symbol_end_date_as_date(self): + self.client.get_transactions(ACCOUNT_HASH, end_date=NOW_DATE) + self.mock_session.get.assert_called_once_with( + self.make_url('/trader/v1/accounts/{accountHash}/transactions'), + params={ + 'types': ','.join(t.value for t in self.client.Transactions.TransactionType), + 'startDate': NOW_DATETIME_MINUS_60_DAYS_ISO, + 'endDate': NOW_DATETIME_TRUNCATED_ISO}) + + + # get_transaction - def test_search_instruments_unchecked(self): + def test_get_transaction(self): + self.client.get_transaction(ACCOUNT_HASH, TRANSACTION_ID) + self.mock_session.get.assert_called_once_with( + self.make_url( + '/trader/v1/accounts/{accountHash}/transactions/{transactionId}'), + params={}) + + + # get_user_preference + + def test_get_user_preferences(self): + self.client.get_user_preferences() + self.mock_session.get.assert_called_once_with( + self.make_url('/trader/v1/userPreference'), params={}) + + + # get_quote + + def test_get_quote(self): + self.client.get_quote(SYMBOL) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/{symbol}/quotes'), params={}) + + + def test_get_quote_fields_single(self): + self.client.get_quote(SYMBOL, fields=self.client.Quote.Fields.QUOTE) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/{symbol}/quotes'), + params={'fields': 'quote'}) + + + def test_get_quote_fields_unchecked(self): self.client.set_enforce_enums(False) - self.client.search_instruments(['AAPL', 'MSFT'], 'fundamental') + self.client.get_quote(SYMBOL, fields=['not-a-field']) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/instruments'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL,MSFT', - 'projection': 'fundamental'}) + self.make_url('/marketdata/v1/{symbol}/quotes'), + params={'fields': 'not-a-field'}) - # get_instrument - - def test_get_instrument(self): - self.client.get_instrument(CUSIP) + def test_get_quote_fields_multiple(self): + self.client.get_quote(SYMBOL, fields=[ + self.client.Quote.Fields.QUOTE, + self.client.Quote.Fields.FUNDAMENTAL]) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/{symbol}/quotes'), + params={'fields': 'quote,fundamental'}) + + + # get_quotes + + def test_get_quotes(self): + self.client.get_quotes(['AAPL', 'MSFT']) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/instruments/{cusip}'), - params={'apikey': API_KEY}) + self.make_url('/marketdata/v1/quotes'), params={ + 'symbols': 'AAPL,MSFT'}) - def test_get_instrument_cusip_must_be_string(self): - msg = 'CUSIPs must be passed as strings to preserve leading zeroes' - with self.assertRaises(ValueError, msg=msg): - self.client.get_instrument(123456) + def test_get_quotes_single_symbol(self): + self.client.get_quotes('AAPL') + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/quotes'), params={ + 'symbols': 'AAPL'}) + + def test_get_quotes_fields(self): + self.client.get_quotes( + ['AAPL', 'MSFT'], + fields=self.client.Quote.Fields.QUOTE) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/quotes'), params={ + 'symbols': 'AAPL,MSFT', + 'fields': 'quote'}) + + + def test_get_quotes_fields_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_quotes(['AAPL', 'MSFT'], fields=['quote']) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/quotes'), params={ + 'symbols': 'AAPL,MSFT', + 'fields': 'quote'}) + + + def test_get_quotes_indicative(self): + self.client.get_quotes(['AAPL', 'MSFT'], indicative=True) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/quotes'), params={ + 'symbols': 'AAPL,MSFT', + 'indicative': 'true'}) - # get_hours_for_multiple_markets + # get_price_history - def test_get_hours_for_multiple_markets_datetime(self): - self.client.get_hours_for_multiple_markets([ - self.client_class.Markets.EQUITY, - self.client_class.Markets.BOND], NOW_DATETIME) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/hours'), params={ - 'apikey': API_KEY, - 'markets': 'EQUITY,BOND', - 'date': NOW_DATE_ISO}) + def test_get_price_history_vanilla(self): + self.client.get_price_history(SYMBOL) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL}) - def test_get_hours_for_multiple_markets_single_market(self): - self.client.get_hours_for_multiple_markets( - self.client_class.Markets.EQUITY, NOW_DATETIME) + def test_get_price_history_period_type(self): + self.client.get_price_history( + SYMBOL, period_type=self.client_class.PriceHistory.PeriodType.MONTH) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/hours'), params={ - 'apikey': API_KEY, - 'markets': 'EQUITY', - 'date': NOW_DATE_ISO}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'periodType': 'month'}) - def test_get_hours_for_multiple_markets_date(self): - self.client.get_hours_for_multiple_markets([ - self.client_class.Markets.EQUITY, - self.client_class.Markets.BOND], NOW_DATE) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/hours'), params={ - 'apikey': API_KEY, - 'markets': 'EQUITY,BOND', - 'date': NOW_DATE_ISO}) + def test_get_price_history_period_type_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_price_history(SYMBOL, period_type='month') + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'periodType': 'month'}) - def test_get_hours_for_multiple_markets_str(self): - with self.assertRaises(ValueError) as cm: - self.client.get_hours_for_multiple_markets([ - self.client_class.Markets.EQUITY, - self.client_class.Markets.BOND], '2020-01-01') - self.assertEqual(str(cm.exception), - "expected type in (datetime.date, datetime.datetime) " - "for date, got 'builtins.str'") + def test_get_price_history_num_periods(self): + self.client.get_price_history( + SYMBOL, period=self.client_class.PriceHistory.Period.TEN_DAYS) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'period': 10}) - def test_get_hours_for_multiple_markets_unchecked(self): + def test_get_price_history_num_periods_unchecked(self): self.client.set_enforce_enums(False) - self.client.get_hours_for_multiple_markets( - ['EQUITY', 'BOND'], NOW_DATETIME) + self.client.get_price_history(SYMBOL, period=10) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/hours'), params={ - 'apikey': API_KEY, - 'markets': 'EQUITY,BOND', - 'date': NOW_DATE_ISO}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'period': 10}) - # get_hours_for_single_market + + def test_get_price_history_frequency_type(self): + self.client.get_price_history( + SYMBOL, + frequency_type=self.client_class.PriceHistory.FrequencyType.DAILY) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'frequencyType': 'daily'}) - def test_get_hours_for_single_market_datetime(self): - self.client.get_hours_for_single_market( - self.client_class.Markets.EQUITY, NOW_DATETIME) + def test_get_price_history_frequency_type_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_price_history(SYMBOL, frequency_type='daily') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/{market}/hours'), params={ - 'apikey': API_KEY, - 'date': NOW_DATE_ISO}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'frequencyType': 'daily'}) - def test_get_hours_for_single_market_date(self): - self.client.get_hours_for_single_market( - self.client_class.Markets.EQUITY, NOW_DATE) + def test_get_price_history_frequency(self): + self.client.get_price_history( + SYMBOL, + frequency=self.client_class.PriceHistory.Frequency.EVERY_FIVE_MINUTES) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/{market}/hours'), params={ - 'apikey': API_KEY, - 'date': NOW_DATE_ISO}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'frequency': 5}) + + + def test_get_price_history_frequency_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_price_history(SYMBOL, frequency=5) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'frequency': 5}) + + + def test_get_price_history_start_datetime(self): + self.client.get_price_history( + SYMBOL, start_datetime=EARLIER_DATETIME) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'startDate': EARLIER_MILLIS}) - def test_get_hours_for_single_market_str(self): + def test_get_price_history_start_datetime_str(self): with self.assertRaises(ValueError) as cm: - self.client.get_hours_for_single_market( - self.client_class.Markets.EQUITY, '2020-01-01') + self.client.get_price_history(SYMBOL, start_datetime='2020-01-01') self.assertEqual(str(cm.exception), - "expected type in (datetime.date, datetime.datetime) for " + - "date, got 'builtins.str'") + "expected type 'datetime.datetime' for " + + "start_datetime, got 'builtins.str'") - def test_get_hours_for_single_market_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_hours_for_single_market('EQUITY', NOW_DATETIME) + def test_get_price_history_end_datetime(self): + self.client.get_price_history(SYMBOL, end_datetime=EARLIER_DATETIME) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/{market}/hours'), params={ - 'apikey': API_KEY, - 'date': NOW_DATE_ISO}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'endDate': EARLIER_MILLIS}) - # get_movers + + def test_get_price_history_end_datetime_str(self): + with self.assertRaises(ValueError) as cm: + self.client.get_price_history(SYMBOL, end_datetime='2020-01-01') + self.assertEqual(str(cm.exception), + "expected type 'datetime.datetime' for " + + "end_datetime, got 'builtins.str'") - def test_get_movers(self): - self.client.get_movers( - INDEX, self.client_class.Movers.Direction.UP, self.client_class.Movers.Change.PERCENT) + def test_get_price_history_need_extended_hours_data(self): + self.client.get_price_history(SYMBOL, need_extended_hours_data=True) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/{index}/movers'), params={ - 'apikey': API_KEY, - 'direction': 'up', - 'change': 'percent'}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'needExtendedHoursData': True}) - - def test_get_movers_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_movers(INDEX, 'up', 'percent') + + def test_get_price_history_need_previous_close(self): + self.client.get_price_history(SYMBOL, need_previous_close=True) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/{index}/movers'), params={ - 'apikey': API_KEY, - 'direction': 'up', - 'change': 'percent'}) + self.make_url('/marketdata/v1/pricehistory'), params={ + 'symbol': SYMBOL, + 'needPreviousClose': True}) - # get_option_chain + # get_option_chain def test_get_option_chain_vanilla(self): self.client.get_option_chain('AAPL') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL'}) - def test_get_option_chain_contract_type(self): self.client.get_option_chain( 'AAPL', contract_type=self.client_class.Options.ContractType.PUT) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'contractType': 'PUT'}) @@ -763,8 +818,7 @@ def test_get_option_chain_contract_type_unchecked(self): self.client.set_enforce_enums(False) self.client.get_option_chain('AAPL', contract_type='PUT') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'contractType': 'PUT'}) @@ -772,27 +826,24 @@ def test_get_option_chain_contract_type_unchecked(self): def test_get_option_chain_strike_count(self): self.client.get_option_chain('AAPL', strike_count=100) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'strikeCount': 100}) - def test_get_option_chain_include_quotes(self): - self.client.get_option_chain('AAPL', include_quotes=True) + def test_get_option_chain_include_underlyingquotes(self): + self.client.get_option_chain('AAPL', include_underlying_quote=True) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', - 'includeQuotes': True}) + 'includeUnderlyingQuote': True}) def test_get_option_chain_strategy(self): self.client.get_option_chain( 'AAPL', strategy=self.client_class.Options.Strategy.STRANGLE) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'strategy': 'STRANGLE'}) @@ -801,8 +852,7 @@ def test_get_option_chain_strategy_unchecked(self): self.client.set_enforce_enums(False) self.client.get_option_chain('AAPL', strategy='STRANGLE') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'strategy': 'STRANGLE'}) @@ -810,8 +860,7 @@ def test_get_option_chain_strategy_unchecked(self): def test_get_option_chain_interval(self): self.client.get_option_chain('AAPL', interval=10.0) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'interval': 10.0}) @@ -819,8 +868,7 @@ def test_get_option_chain_interval(self): def test_get_option_chain_strike(self): self.client.get_option_chain('AAPL', strike=123) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'strike': 123}) @@ -829,8 +877,7 @@ def test_get_option_chain_strike_range(self): self.client.get_option_chain( 'AAPL', strike_range=self.client_class.Options.StrikeRange.IN_THE_MONEY) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'range': 'ITM'}) @@ -839,8 +886,7 @@ def test_get_option_chain_strike_range_unchecked(self): self.client.set_enforce_enums(False) self.client.get_option_chain('AAPL', strike_range='ITM') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'range': 'ITM'}) @@ -849,8 +895,7 @@ def test_get_option_chain_from_date_datetime(self): self.client.get_option_chain( 'AAPL', from_date=NOW_DATETIME) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'fromDate': NOW_DATE_ISO}) @@ -858,8 +903,7 @@ def test_get_option_chain_from_date_datetime(self): def test_get_option_chain_from_date_date(self): self.client.get_option_chain('AAPL', from_date=NOW_DATE) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'fromDate': NOW_DATE_ISO}) @@ -868,15 +912,14 @@ def test_get_option_chain_from_date_str(self): with self.assertRaises(ValueError) as cm: self.client.get_option_chain('AAPL', from_date='2020-01-01') self.assertEqual(str(cm.exception), - "expected type in (datetime.date, datetime.datetime) for " + + "expected type 'datetime.date' for " + "from_date, got 'builtins.str'") def test_get_option_chain_to_date_datetime(self): self.client.get_option_chain('AAPL', to_date=NOW_DATETIME) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'toDate': NOW_DATE_ISO}) @@ -884,8 +927,7 @@ def test_get_option_chain_to_date_datetime(self): def test_get_option_chain_to_date_date(self): self.client.get_option_chain('AAPL', to_date=NOW_DATE) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, + self.make_url('/marketdata/v1/chains'), params={ 'symbol': 'AAPL', 'toDate': NOW_DATE_ISO}) @@ -894,222 +936,98 @@ def test_get_option_chain_to_date_str(self): with self.assertRaises(ValueError) as cm: self.client.get_option_chain('AAPL', to_date='2020-01-01') self.assertEqual(str(cm.exception), - "expected type in (datetime.date, datetime.datetime) for " + - "to_date, got 'builtins.str'") - - - def test_get_option_chain_volatility(self): - self.client.get_option_chain('AAPL', volatility=40.0) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'volatility': 40.0}) - - - def test_get_option_chain_underlying_price(self): - self.client.get_option_chain('AAPL', underlying_price=234.0) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'underlyingPrice': 234.0}) - - - def test_get_option_chain_interest_rate(self): - self.client.get_option_chain('AAPL', interest_rate=0.07) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'interestRate': 0.07}) - - - def test_get_option_chain_days_to_expiration(self): - self.client.get_option_chain('AAPL', days_to_expiration=12) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'daysToExpiration': 12}) - - - def test_get_option_chain_exp_month(self): - self.client.get_option_chain( - 'AAPL', exp_month=self.client_class.Options.ExpirationMonth.JANUARY) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'expMonth': 'JAN'}) - - - def test_get_option_chain_exp_month_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_option_chain('AAPL', exp_month='JAN') - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'expMonth': 'JAN'}) - - - def test_get_option_chain_option_type(self): - self.client.get_option_chain( - 'AAPL', option_type=self.client_class.Options.Type.STANDARD) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'optionType': 'S'}) - - - def test_get_option_chain_option_type_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_option_chain('AAPL', option_type='S') - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/chains'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL', - 'optionType': 'S'}) - ''' - - # get_price_history - - - def test_get_price_history_vanilla(self): - self.client.get_price_history(SYMBOL) - self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL}) - - - def test_get_price_history_period_type(self): - self.client.get_price_history( - SYMBOL, period_type=self.client_class.PriceHistory.PeriodType.MONTH) - self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'periodType': 'month'}) + "expected type 'datetime.date' for " + + "to_date, got 'builtins.str'") - def test_get_price_history_period_type_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_price_history(SYMBOL, period_type='month') + def test_get_option_chain_volatility(self): + self.client.get_option_chain('AAPL', volatility=40.0) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'periodType': 'month'}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'volatility': 40.0}) - def test_get_price_history_num_periods(self): - self.client.get_price_history( - SYMBOL, period=self.client_class.PriceHistory.Period.TEN_DAYS) + def test_get_option_chain_underlying_price(self): + self.client.get_option_chain('AAPL', underlying_price=234.0) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'period': 10}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'underlyingPrice': 234.0}) - def test_get_price_history_num_periods_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_price_history(SYMBOL, period=10) + def test_get_option_chain_interest_rate(self): + self.client.get_option_chain('AAPL', interest_rate=0.07) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'period': 10}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'interestRate': 0.07}) - def test_get_price_history_frequency_type(self): - self.client.get_price_history( - SYMBOL, - frequency_type=self.client_class.PriceHistory.FrequencyType.DAILY) + def test_get_option_chain_days_to_expiration(self): + self.client.get_option_chain('AAPL', days_to_expiration=12) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'frequencyType': 'daily'}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'daysToExpiration': 12}) - def test_get_price_history_frequency_type_unchecked(self): - self.client.set_enforce_enums(False) - self.client.get_price_history(SYMBOL, frequency_type='daily') + def test_get_option_chain_exp_month(self): + self.client.get_option_chain( + 'AAPL', exp_month=self.client_class.Options.ExpirationMonth.JANUARY) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'frequencyType': 'daily'}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'expMonth': 'JAN'}) - def test_get_price_history_frequency(self): - self.client.get_price_history( - SYMBOL, - frequency=self.client_class.PriceHistory.Frequency.EVERY_FIVE_MINUTES) - self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'frequency': 5}) - - - def test_get_price_history_frequency_unchecked(self): + def test_get_option_chain_exp_month_unchecked(self): self.client.set_enforce_enums(False) - self.client.get_price_history(SYMBOL, frequency=5) + self.client.get_option_chain('AAPL', exp_month='JAN') self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'frequency': 5}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'expMonth': 'JAN'}) - def test_get_price_history_start_datetime(self): - self.client.get_price_history( - SYMBOL, start_datetime=EARLIER_DATETIME) + def test_get_option_chain_option_type(self): + self.client.get_option_chain( + 'AAPL', option_type=self.client_class.Options.Type.STANDARD) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'startDate': EARLIER_MILLIS}) - - - def test_get_price_history_start_datetime_str(self): - with self.assertRaises(ValueError) as cm: - self.client.get_price_history(SYMBOL, start_datetime='2020-01-01') - self.assertEqual(str(cm.exception), - "expected type 'datetime.datetime' for " + - "start_datetime, got 'builtins.str'") + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'optionType': 'S'}) - def test_get_price_history_end_datetime(self): - self.client.get_price_history(SYMBOL, end_datetime=EARLIER_DATETIME) + def test_get_option_chain_option_type_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_option_chain('AAPL', option_type='S') self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'endDate': EARLIER_MILLIS}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'optionType': 'S'}) - - def test_get_price_history_end_datetime_str(self): - with self.assertRaises(ValueError) as cm: - self.client.get_price_history(SYMBOL, end_datetime='2020-01-01') - self.assertEqual(str(cm.exception), - "expected type 'datetime.datetime' for " + - "end_datetime, got 'builtins.str'") - - def test_get_price_history_need_extended_hours_data(self): - self.client.get_price_history(SYMBOL, need_extended_hours_data=True) + def test_get_option_chain_option_entitlement(self): + self.client.get_option_chain( + 'AAPL', entitlement=self.client.Options.Entitlement.PAYING_PRO) self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'needExtendedHoursData': True}) + self.make_url('/marketdata/v1/chains'), params={ + 'symbol': 'AAPL', + 'entitlement': 'PP'}) - def test_get_price_history_need_previous_close(self): - self.client.get_price_history(SYMBOL, need_previous_close=True) + # get_option_expiration_chain + + def test_get_option_expiration_chain(self): + self.client.get_option_expiration_chain('AAPL') self.mock_session.get.assert_called_once_with( - self.make_url('/marketdata/v1/pricehistory'), params={ - 'symbol': SYMBOL, - 'needPreviousClose': True}) + self.make_url('/marketdata/v1/expirationchain'), + params={'symbol': 'AAPL'}) # get_price_history_every_minute - @patch('schwab.client.base.datetime.datetime', mockdatetime) def test_get_price_history_every_minute_vanilla(self): self.client.get_price_history_every_minute('AAPL') @@ -2109,355 +2027,143 @@ def test_get_price_history_every_week_previous_close(self): params=params) - ''' - # get_quote - - - def test_get_quote(self): - self.client.get_quote(SYMBOL) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/{symbol}/quotes'), params={ - 'apikey': API_KEY}) - - # get_quotes - - - def test_get_quotes(self): - self.client.get_quotes(['AAPL', 'MSFT']) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/quotes'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL,MSFT'}) + # get_movers - def test_get_quotes_single_symbol(self): - self.client.get_quotes('AAPL') + def test_get_movers(self): + self.client.get_movers( + self.client.Movers.Index.DJI) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/marketdata/quotes'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL'}) - - # get_transaction + self.make_url('/marketdata/v1/movers/$DJI'), params={}) - - def test_get_transaction(self): - self.client.get_transaction(ACCOUNT_HASH, TRANSACTION_ID) - self.mock_session.get.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/transactions/{transactionId}'), - params={'apikey': API_KEY}) - - def test_get_transaction_str(self): - self.client.get_transaction(str(ACCOUNT_HASH), str(TRANSACTION_ID)) + def test_get_movers_index_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_movers('not-an-index') self.mock_session.get.assert_called_once_with( - self.make_url( - '/v1/accounts/{accountHash}/transactions/{transactionId}'), - params={'apikey': API_KEY}) + self.make_url('/marketdata/v1/movers/not-an-index'), params={}) - # get_transactions - - - def test_get_transactions(self): - self.client.get_transactions(ACCOUNT_HASH) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY}) - - def test_get_transactions_str(self): - self.client.get_transactions(str(ACCOUNT_HASH)) + def test_get_movers_sort_order(self): + self.client.get_movers( + self.client.Movers.Index.DJI, + sort_order=self.client.Movers.SortOrder.VOLUME) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY}) + self.make_url('/marketdata/v1/movers/$DJI'), + params={'sort': 'VOLUME'}) - - def test_get_transactions_type(self): - self.client.get_transactions( - ACCOUNT_HASH, - transaction_type=self.client_class.Transactions.TransactionType.DIVIDEND) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'type': 'DIVIDEND'}) - - def test_get_transactions_type_unchecked(self): + def test_get_movers_sort_order_unchecked(self): self.client.set_enforce_enums(False) - self.client.get_transactions(ACCOUNT_HASH, transaction_type='DIVIDEND') - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'type': 'DIVIDEND'}) - - - def test_get_transactions_symbol(self): - self.client.get_transactions(ACCOUNT_HASH, symbol='AAPL') - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'symbol': 'AAPL'}) - - - def test_get_transactions_start_date_datetime(self): - self.client.get_transactions(ACCOUNT_HASH, start_date=NOW_DATETIME) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'startDate': NOW_DATE_ISO}) - - - def test_get_transactions_start_date_date(self): - self.client.get_transactions(ACCOUNT_HASH, start_date=NOW_DATE) + self.client.get_movers( + self.client.Movers.Index.DJI, + sort_order='not-a-sort-order') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'startDate': NOW_DATE_ISO}) - - - def test_get_transactions_start_date_str(self): - with self.assertRaises(ValueError) as cm: - self.client.get_transactions(ACCOUNT_HASH, start_date='2020-01-01') - self.assertEqual(str(cm.exception), - "expected type in (datetime.date, datetime.datetime) for " + - "start_date, got 'builtins.str'") + self.make_url('/marketdata/v1/movers/$DJI'), + params={'sort': 'not-a-sort-order'}) - - def test_get_transactions_end_date(self): - self.client.get_transactions(ACCOUNT_HASH, end_date=NOW_DATETIME) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'endDate': NOW_DATE_ISO}) - - def test_get_transactions_end_date_datetime(self): - self.client.get_transactions(ACCOUNT_HASH, end_date=NOW_DATETIME) + def test_get_movers_frequency(self): + self.client.get_movers( + self.client.Movers.Index.DJI, + frequency=self.client.Movers.Frequency.ZERO) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/transactions'), params={ - 'apikey': API_KEY, - 'endDate': NOW_DATE_ISO}) - - - def test_get_transactions_end_date_str(self): - with self.assertRaises(ValueError) as cm: - self.client.get_transactions(ACCOUNT_HASH, end_date='2020-01-01') - self.assertEqual(str(cm.exception), - "expected type in (datetime.date, datetime.datetime) for " + - "end_date, got 'builtins.str'") - - # get_preferences + self.make_url('/marketdata/v1/movers/$DJI'), + params={'frequency': '0'}) - - def test_get_preferences(self): - self.client.get_preferences(ACCOUNT_HASH) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/preferences'), params={ - 'apikey': API_KEY}) - - def test_get_preferences_str(self): - self.client.get_preferences(str(ACCOUNT_HASH)) + def test_get_movers_frequency_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_movers( + self.client.Movers.Index.DJI, + frequency='999999') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/preferences'), params={ - 'apikey': API_KEY}) - - # get_streamer_subscription_keys + self.make_url('/marketdata/v1/movers/$DJI'), + params={'frequency': '999999'}) - - def test_get_streamer_subscription_keys(self): - self.client.get_streamer_subscription_keys([1000, 2000, 3000]) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals/streamersubscriptionkeys'), - params={ - 'apikey': API_KEY, - 'accountHashs': '1000,2000,3000'}) - - def test_get_streamer_subscription_keys_one_account_id(self): - self.client.get_streamer_subscription_keys(1000) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals/streamersubscriptionkeys'), - params={ - 'apikey': API_KEY, - 'accountHashs': '1000'}) + # get_market_hours - def test_get_streamer_subscription_keys_str(self): - self.client.get_streamer_subscription_keys(['1000', '2000', '3000']) + def test_get_market_hours_single_market(self): + self.client.get_market_hours( + self.client_class.MarketHours.Market.EQUITY) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals/streamersubscriptionkeys'), - params={ - 'apikey': API_KEY, - 'accountHashs': '1000,2000,3000'}) - - # get_user_principals + self.make_url('/marketdata/v1/markets'), params={ + 'markets': 'equity'}) - - def test_get_user_principals_vanilla(self): - self.client.get_user_principals() - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals'), params={ - 'apikey': API_KEY}) - - def test_get_user_principals_fields(self): - self.client.get_user_principals( - fields=[ - self.client_class.UserPrincipals.Fields.STREAMER_SUBSCRIPTION_KEYS, - self.client_class.UserPrincipals.Fields.PREFERENCES]) + def test_get_market_hours_market_list(self): + self.client.get_market_hours( + [self.client_class.MarketHours.Market.EQUITY, + self.client_class.MarketHours.Market.OPTION]) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals'), params={ - 'apikey': API_KEY, - 'fields': 'streamerSubscriptionKeys,preferences'}) + self.make_url('/marketdata/v1/markets'), params={ + 'markets': 'equity,option'}) - - def test_get_user_principals_one_field(self): - self.client.get_user_principals( - fields=self.client_class.UserPrincipals.Fields.PREFERENCES) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals'), params={ - 'apikey': API_KEY, - 'fields': 'preferences'}) - - def test_get_user_principals_fields_unchecked(self): + def test_get_market_hours_market_unchecked(self): self.client.set_enforce_enums(False) - self.client.get_user_principals( - fields=['streamerSubscriptionKeys', 'preferences']) + self.client.get_market_hours(['not-a-market']) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/userprincipals'), params={ - 'apikey': API_KEY, - 'fields': 'streamerSubscriptionKeys,preferences'}) - - # update_preferences - - - def test_update_preferences(self): - preferences = {'wantMoney': True} - self.client.update_preferences(ACCOUNT_HASH, preferences) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/preferences'), - json=preferences) - - - def test_update_preferences_str(self): - preferences = {'wantMoney': True} - self.client.update_preferences(str(ACCOUNT_HASH), preferences) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/preferences'), - json=preferences) - - # create_watchlist - - - def test_create_watchlist(self): - watchlist = {'AAPL': True} - self.client.create_watchlist(ACCOUNT_HASH, watchlist) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists'), - json=watchlist) - - - def test_create_watchlist_str(self): - watchlist = {'AAPL': True} - self.client.create_watchlist(str(ACCOUNT_HASH), watchlist) - self.mock_session.post.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists'), - json=watchlist) + self.make_url('/marketdata/v1/markets'), params={ + 'markets': 'not-a-market'}) - # delete_watchlist - - def test_delete_watchlist(self): - watchlist = {'AAPL': True} - self.client.delete_watchlist(ACCOUNT_HASH, WATCHLIST_ID) - self.mock_session.delete.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}')) + def test_get_market_hours_date(self): + self.client.get_market_hours( + self.client_class.MarketHours.Market.EQUITY, + date=NOW_DATE) + self.mock_session.get.assert_called_once_with( + self.make_url('/marketdata/v1/markets'), params={ + 'markets': 'equity', + 'date': NOW_DATE_ISO}) - - def test_delete_watchlist_str(self): - watchlist = {'AAPL': True} - self.client.delete_watchlist(str(ACCOUNT_HASH), str(WATCHLIST_ID)) - self.mock_session.delete.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}')) - # get_watchlist + def test_get_market_hours_date_str(self): + with self.assertRaises(ValueError) as cm: + self.client.get_market_hours( + self.client_class.MarketHours.Market.EQUITY, + date='2020-01-02') + self.assertEqual(str(cm.exception), + "expected type 'datetime.date' for " + + "date, got 'builtins.str'") - - def test_get_watchlist(self): - self.client.get_watchlist(ACCOUNT_HASH, WATCHLIST_ID) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}'), - params={}) + # get_instruments - def test_get_watchlist_str(self): - self.client.get_watchlist(str(ACCOUNT_HASH), str(WATCHLIST_ID)) + def test_get_instruments(self): + self.client.get_instruments( + ['AAPL', 'MSFT'], self.client_class.Instrument.Projection.FUNDAMENTAL) self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}'), - params={}) + self.make_url('/marketdata/v1/instruments'), params={ + 'symbol': 'AAPL,MSFT', + 'projection': 'fundamental'}) - # get_watchlists_for_multiple_accounts - - def test_get_watchlists_for_multiple_accounts(self): - self.client.get_watchlists_for_multiple_accounts() + def test_get_instruments_projection_unchecked(self): + self.client.set_enforce_enums(False) + self.client.get_instruments( + ['AAPL', 'MSFT'], 'not-a-projection') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/watchlists'), params={}) - - # get_watchlists_for_single_account + self.make_url('/marketdata/v1/instruments'), params={ + 'symbol': 'AAPL,MSFT', + 'projection': 'not-a-projection'}) - - def test_get_watchlists_for_single_account(self): - self.client.get_watchlists_for_single_account(ACCOUNT_HASH) - self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists'), params={}) + # get_instrument_by_cusip - def test_get_watchlists_for_single_account_str(self): - self.client.get_watchlists_for_single_account(str(ACCOUNT_HASH)) + def test_get_instrument_by_cusip(self): + self.client.get_instrument_by_cusip('037833100') self.mock_session.get.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists'), params={}) - - # replace_watchlist - - - def test_replace_watchlist(self): - watchlist = {'AAPL': True} - self.client.replace_watchlist(ACCOUNT_HASH, WATCHLIST_ID, watchlist) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}'), - json=watchlist) - - - def test_replace_watchlist_str(self): - watchlist = {'AAPL': True} - self.client.replace_watchlist( - str(ACCOUNT_HASH), str(WATCHLIST_ID), watchlist) - self.mock_session.put.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}'), - json=watchlist) + self.make_url('/marketdata/v1/instruments/037833100'), params={}) - # update_watchlist - - def test_update_watchlist(self): - watchlist = {'AAPL': True} - self.client.update_watchlist(ACCOUNT_HASH, WATCHLIST_ID, watchlist) - self.mock_session.patch.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}'), - json=watchlist) + def test_get_instrument_by_cusip_cusip_must_be_string(self): + with self.assertRaises(ValueError) as cm: + self.client.get_instrument_by_cusip(37833100) + self.assertEqual(str(cm.exception), 'cusip must be passed as str') - - def test_update_watchlist_str(self): - watchlist = {'AAPL': True} - self.client.update_watchlist( - str(ACCOUNT_HASH), str(WATCHLIST_ID), watchlist) - self.mock_session.patch.assert_called_once_with( - self.make_url('/v1/accounts/{accountHash}/watchlists/{watchlistId}'), - json=watchlist) - ''' class ClientTest(_TestClient, unittest.TestCase): """