class tdAction(object): def __init__(self): """Constructor""" self.conKey = CONSUMER_KEY self.redirect = REDIRECT_URL self.acct = ACCT_NUM self.tdClient = TDClient(client_id=self.conKey, redirect_uri=self.redirect) self.tdClient.login() def quote(self, tickerList): """Grab quotes""" retDict = {} quotes = self.tdClient.get_quotes(instruments=tickerList) for key in quotes: if type(quotes[key]['lastPrice']) is not None: retDict[key] = quotes[key]['lastPrice'] return retDict def history(self, tickerList): """Create real time data for a given list of tickers""" retDict = {} for item in tickerList: tickerHistory = self.tdClient.get_price_history(symbol=item, period_type='year') priceHistory = [] for data in tickerHistory['candles']: priceHistory.append(data['close']) df = DataFrame({item: priceHistory}) retDict[item] = df return retDict
from datetime import timedelta from td.client import TDClient # Create a new session TDSession = TDClient( client_id='<CLIENT_ID>', redirect_uri='<REDIRECT_URI>', credentials_path='<CREDENTIALS_PATH>' ) # Login to the session TDSession.login() # `get_quotes` endpoint with single value. Should not return an error. quotes_single = TDSession.get_quotes(instruments=['SQ']) # `get_quotes` with a Options Contract quotes_option_contract = TDSession.get_quotes(instruments=['MSFT_041720C75']) # `get_quotes` with a Futures Contract quotes_futures = TDSession.get_quotes(instruments=['/ES']) # `get_quotes` with Forex quotes_forex = TDSession.get_quotes(instruments=['AUD/USD']) # `get_quotes` endpoint with multiple values quotes_multi = TDSession.get_quotes(instruments=['SQ', 'MSFT']) # `search_instruments` Endpoint instrument_search_data = TDSession.search_instruments(
class TDSession(TestCase): """Will perform a unit test for the TD session.""" def setUp(self) -> None: """Set up the `TDClient`.""" # Grab configuration values. config = ConfigParser() config.read('config/config.ini') # Load the values. CLIENT_ID = config.get('main', 'CLIENT_ID') REDIRECT_URI = config.get('main', 'REDIRECT_URI') JSON_PATH = config.get('main', 'JSON_PATH') ACCOUNT_NUMBER = config.get('main', 'ACCOUNT_NUMBER') # Initalize the session. self.td_session = TDClient(client_id=CLIENT_ID, redirect_uri=REDIRECT_URI, credentials_path=JSON_PATH, account_number=ACCOUNT_NUMBER) def test_creates_instance_of_session(self): """Create an instance and make sure it's a `TDClient` object.""" self.assertIsInstance(self.td_session, TDClient) def test_login(self): """Test whether the session is authenticated or not.""" self.assertTrue(self.td_session.login()) self.assertTrue(self.td_session.authstate) def test_state(self): """Make sure the state is updated.""" self.assertIsNotNone(self.td_session.state['refresh_token']) self.assertIsNotNone(self.td_session.state['access_token']) self.assertNotEqual(self.td_session.state['refresh_token_expires_at'], 0) self.assertNotEqual(self.td_session.state['access_token_expires_at'], 0) def test_single_get_quotes(self): """Test Getting a Single quote.""" # Grab a single quote. quotes = self.td_session.get_quotes(instruments=['MSFT']) # See if the Symbol is in the Quotes. self.assertIn('MSFT', quotes) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_single_quotes.jsonc', 'w+') as data_file: json.dump(obj=quotes, fp=data_file, indent=3) def test_get_quotes(self): """Test Getting Multiple Quotes.""" # Grab multiple Quotes. quotes = self.td_session.get_quotes(instruments=['MSFT', 'AAPL']) # See if the Symbols are in the Quotes. self.assertTrue(set(['MSFT', 'AAPL']).issuperset(set(quotes.keys()))) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_multiple_quotes.jsonc', 'w+') as data_file: json.dump(obj=quotes, fp=data_file, indent=3) def test_get_accounts(self): """Test Get Accounts.""" accounts = self.td_session.get_accounts(account='all', fields=['orders', 'positions']) self.assertIn('positions', accounts[0]['securitiesAccount']) self.assertIn('currentBalances', accounts[0]['securitiesAccount']) # self.assertIn('orderStrategies', accounts[0]['securitiesAccount']) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_accounts.jsonc', 'w+') as data_file: json.dump(obj=accounts, fp=data_file, indent=3) def test_create_stream_session(self): """Test Creating a new streaming session.""" stream_session = self.td_session.create_streaming_session() self.assertIsInstance(stream_session, TDStreamerClient) def test_get_transactions(self): """Test getting transactions.""" # `get_transactions` Endpoint. Should not return an error transaction_data_multi = self.td_session.get_transactions( account=self.td_session.account_number, transaction_type='ALL') # Make sure it's a list. self.assertIsInstance(transaction_data_multi, list) self.assertIn('type', transaction_data_multi[0]) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_transaction_data.jsonc', 'w+') as data_file: json.dump(obj=transaction_data_multi, fp=data_file, indent=3) def test_get_market_hours(self): """Test get market hours.""" # `get_market_hours` Endpoint with multiple values market_hours_multi = self.td_session.get_market_hours( markets=['EQUITY', 'FOREX'], date=datetime.today().isoformat()) # If it's a weekend nothing is returned, so raise an error. if datetime.today().weekday() in (5, 6): # Make sure it's a list. self.assertIsInstance(market_hours_multi, dict) self.assertIn('isOpen', market_hours_multi['equity']['equity']) else: # Make sure it's a list. self.assertIsInstance(market_hours_multi, dict) self.assertIn('isOpen', market_hours_multi['equity']['EQ']) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_market_hours.jsonc', 'w+') as data_file: json.dump(obj=market_hours_multi, fp=data_file, indent=3) def test_get_instrument(self): """Test getting an instrument.""" # `get_instruments` Endpoint. get_instrument = self.td_session.get_instruments(cusip='594918104') # Make sure it's a list. self.assertIsInstance(get_instrument, list) self.assertIn('cusip', get_instrument[0]) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_instrument.jsonc', 'w+') as data_file: json.dump(obj=get_instrument, fp=data_file, indent=3) def test_chart_history(self): """Test getting historical prices.""" # Define a list of all valid periods valid_values = { 'minute': { 'day': [1, 2, 3, 4, 5, 10] }, 'daily': { 'month': [1, 2, 3, 6], 'year': [1, 2, 3, 5, 10, 15, 20], 'ytd': [1] }, 'weekly': { 'month': [1, 2, 3, 6], 'year': [1, 2, 3, 5, 10, 15, 20], 'ytd': [1] }, 'monthly': { 'year': [1, 2, 3, 5, 10, 15, 20] } } # Define the static arguments. hist_symbol = 'MSFT' hist_needExtendedHoursData = False for frequency_type in valid_values.keys(): frequency_periods = valid_values[frequency_type] for frequency_period in frequency_periods.keys(): possible_values = frequency_periods[frequency_period] for value in possible_values: # Define the dynamic arguments - I want 5 DAYS of historical 1-minute bars. hist_periodType = frequency_period hist_period = value hist_frequencyType = frequency_type hist_frequency = 1 # make the request historical_prices = self.td_session.get_price_history( symbol=hist_symbol, period_type=hist_periodType, period=hist_period, frequency_type=hist_frequencyType, frequency=hist_frequency, extended_hours=hist_needExtendedHoursData) self.assertIsInstance(historical_prices, dict) self.assertFalse(historical_prices['empty']) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_historical_prices.jsonc', 'w+') as data_file: json.dump(obj=historical_prices, fp=data_file, indent=3) def test_custom_historical_prices(self): """Test getting historical prices for a custom date range.""" # The max look back period for minute data is 31 Days. lookback_period = 10 # Define today. today_00 = datetime.now() # Define 300 days ago. today_ago = datetime.now() - timedelta(days=lookback_period) # The TD API expects a timestamp in milliseconds. However, the timestamp() method only returns to seconds so multiply it by 1000. today_00 = str(int(round(today_00.timestamp() * 1000))) today_ago = str(int(round(today_ago.timestamp() * 1000))) # These values will now be our startDate and endDate parameters. hist_startDate = today_ago hist_endDate = today_00 # Define the dynamic arguments. hist_periodType = 'day' hist_frequencyType = 'minute' hist_frequency = 1 # Make the request historical_custom = self.td_session.get_price_history( symbol='MSFT', period_type=hist_periodType, frequency_type=hist_frequencyType, start_date=hist_startDate, end_date=hist_endDate, frequency=hist_frequency, extended_hours=True) self.assertIsInstance(historical_custom, dict) self.assertFalse(historical_custom['empty']) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_historical_prices.jsonc', 'w+') as data_file: json.dump(obj=historical_custom, fp=data_file, indent=3) def test_search_instruments(self): """Test Searching for Instruments.""" # `search_instruments` Endpoint instrument_search_data = self.td_session.search_instruments( symbol='MSFT', projection='symbol-search') self.assertIsInstance(instrument_search_data, dict) self.assertIn('MSFT', instrument_search_data) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_search_instrument.jsonc', 'w+') as data_file: json.dump(obj=instrument_search_data, fp=data_file, indent=3) def test_get_movers(self): """Test getting Market movers.""" # `get_movers` Endpoint movers_data = self.td_session.get_movers(market='$DJI', direction='up', change='value') if datetime.today().weekday() in (5, 6): self.assertIsInstance(movers_data, list) self.assertFalse(movers_data) else: self.assertIsInstance(movers_data, list) self.assertIn('symbol', movers_data[0]) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_movers.jsonc', 'w+') as data_file: json.dump(obj=movers_data, fp=data_file, indent=3) def test_get_user_preferences(self): """Test getting user preferences.""" # `get_preferences` endpoint. Should not return an error preference_data = self.td_session.get_preferences( account=self.td_session.account_number) self.assertIsInstance(preference_data, dict) self.assertIn('expressTrading', preference_data) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_account_preferences.jsonc', 'w+') as data_file: json.dump(obj=preference_data, fp=data_file, indent=3) def test_get_user_principals(self): """Test getting user principals.""" # `get_preferences` endpoint. Should not return an error user_principals = self.td_session.get_user_principals( fields=['preferences', 'surrogateIds']) self.assertIsInstance(user_principals, dict) self.assertIn('userId', user_principals) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_user_principals.jsonc', 'w+') as data_file: json.dump(obj=user_principals, fp=data_file, indent=3) def test_get_streamer_keys(self): """Test getting user preferences.""" # `get_subscription_keys` endpoint. Should not return an error streamer_keys = self.td_session.get_streamer_subscription_keys( accounts=[self.td_session.account_number]) self.assertIsInstance(streamer_keys, dict) self.assertIn('keys', streamer_keys) # Save the data. if SAVE_FLAG: with open('samples/responses/sample_streamer_keys.jsonc', 'w+') as data_file: json.dump(obj=streamer_keys, fp=data_file, indent=3) def tearDown(self) -> None: """Teardown the Robot.""" self.td_session = None
# to the same location. # # See the String Representation of the TDClient Session Object. print(TDSession) # ------------------ # # ENDPOINT TESTING - GET QUOTES # # NOTES: The maximum number of quotes you may request at once is 500. # # ------------------ # TEST - `get_quotes` endpoint with single value. Should not return an error. quotes_single = TDSession.get_quotes(instruments=['SQ']) # TEST - `get_quotes` endpoint with multiple values. Should not return an error. quotes_multi = TDSession.get_quotes(instruments=['SQ', 'MSFT']) # PRINT - `get_quotes` # pprint.pprint(quotes_single) # pprint.pprint(quotes_multi) # ------------------ # # ENDPOINT TESTING - HISTORICAL PRICES # # NOTES: # 1. For historical 1-minute bar data, you can get 1 day, 2 day, 3 day, 4 day, 5 day, or 10 day. #
from pprint import pprint from configparser import ConfigParser from td.client import TDClient # Grab configuration values. config = ConfigParser() config.read('config/config.ini') CLIENT_ID = config.get('main', 'CLIENT_ID') REDIRECT_URI = config.get('main', 'REDIRECT_URI') JSON_PATH = config.get('main', 'JSON_PATH') ACCOUNT_NUMBER = config.get('main', 'ACCOUNT_NUMBER') # Create a new session TDSession = TDClient(client_id=CLIENT_ID, redirect_uri=REDIRECT_URI, credentials_path=JSON_PATH, account_number=ACCOUNT_NUMBER) # Login to the session TDSession.login() pprint(TDSession.get_quotes(instruments=['MSFT']))
# Import the client from td.client import TDClient from config import account_number, client_id # Create a new session, credentials path is optional. TDSession = TDClient(account_number=account_number, client_id=client_id, redirect_uri='http://localhost/TDtest', credentials_path='credentials.json') # Login to the session TDSession.login() # Grab real-time quotes for 'MSFT' (Microsoft) msft_quotes = TDSession.get_quotes(instruments=['TSLA']) # Grab real-time quotes for 'AMZN' (Amazon) and 'SQ' (Square) multiple_quotes = TDSession.get_quotes(instruments=['AMZN,SQ']) multiple_quotes['AMZN'] #print(msft_quotes) print(multiple_quotes['AMZN'])
class AmeritradeRebalanceUtils: def __init__(self): self.session = None self.account = None self.account_id = None def auth(self, credentials_path='./td_state.json', client_path='./td_client_auth.json'): with open(client_path) as f: data = json.load(f) self.session = TDClient( client_id=data['client_id'], redirect_uri=data['callback_url'], credentials_path=credentials_path ) self.session.login() # assuming only 1 account under management self.account = self.session.get_accounts(fields=['positions'])[0] self.account_id = self.account['securitiesAccount']['accountId'] return self.session def get_portfolio(self): positions = self.account['securitiesAccount']['positions'] portfolio = {} for position in positions: portfolio[position['instrument']['symbol']] = position['longQuantity'] return portfolio def place_orders_dry_run(self, portfolio_diff: dict): result = portfolio_diff.copy() prices = self._get_last_prices(result) for ticker, qty in portfolio_diff.items(): round_qty = round(qty) abs_rounded_qty = abs(round_qty) result[ticker] = { 'instruction': ('BUY' if qty > 0 else 'SELL'), 'qty': abs_rounded_qty, 'money_movement': round_qty*prices[ticker]*-1 } return result def place_orders(self, place_orders_dry_run: dict): result = [] for ticker, order in place_orders_dry_run.items(): res = self.session.place_order(account=self.account_id, order=self._get_market_order_payload(ticker, order['qty'], order['instruction'])) result.append(res) return result def _get_market_order_payload(self, ticker, quantity, instruction='BUY'): return { "orderType": "MARKET", "session": "NORMAL", "duration": "DAY", "orderStrategyType": "SINGLE", "orderLegCollection": [ { "instruction": instruction, "quantity": quantity, "instrument": { "symbol": ticker, "assetType": "EQUITY" } } ] } def _get_last_prices(self, portfolio: dict): quotes = self.session.get_quotes(instruments=portfolio.keys()) portfolio_prices = portfolio.copy() for ticker, _ in portfolio_prices.items(): portfolio_prices[ticker] = quotes[ticker]['lastPrice'] return portfolio_prices
My advice to you is to keep the your "TDAmeritradeState.json" in the same location and make sure any scripts that use it all point to the same location. ''' # See the String Representation of the TDClient Session Object. print(TDSession) ''' ENDPOINT TESTING - GET QUOTES NOTES: The maximum number of quotes you may request at once is 500. ''' # TEST - `get_quotes` endpoint with single value. Should not return an error. quotes_single = TDSession.get_quotes(instruments=['/ES']) # TEST - `get_quotes` endpoint with multiple values. Should not return an error. quotes_multi = TDSession.get_quotes(instruments=['SQ', 'MSFT']) # PRINT - `get_quotes` pprint.pprint(quotes_single) pprint.pprint(quotes_multi) ''' ENDPOINT TESTING - HISTORICAL PRICES NOTES: 1. For historical 1-minute bar data, you can get 1 day, 2 day, 3 day, 4 day, 5 day, or 10 day. ''' #