def get_stock_info(self): print("Getting stock data from Robinhood") trader = Robinhood() quotes = zip(self.stock_list, trader.quotes_data(self.stock_list)) return quotes
class RobinhoodShell(cmd.Cmd): intro = 'Welcome to the Robinhood shell. Type help or ? to list commands.\n' prompt = '> ' # API Object trader = None # Cache file used to store instrument cache instruments_cache_file = 'instruments.data' # Maps Symbol to Instrument URL instruments_cache = {} # Maps URL to Symbol instruments_reverse_cache = {} def __init__(self): cmd.Cmd.__init__(self) self.trader = Robinhood() self.trader.login(username=USERNAME, password=PASSWORD) try: data = open('instruments.data').read() self.instruments_cache = json.loads(data) for k in self.instruments_cache: self.instruments_reverse_cache[self.instruments_cache[k]] = k except: pass # ----- basic commands ----- def do_l(self, arg): 'Lists current portfolio' portfolio = self.trader.portfolios() print 'Equity Value:', portfolio['equity'] account_details = self.trader.get_account() if 'margin_balances' in account_details: print 'Buying Power:', account_details['margin_balances'][ 'unallocated_margin_cash'] positions = self.trader.securities_owned() symbols = [] for position in positions['results']: symbol = self.get_symbol(position['instrument']) symbols.append(symbol) raw_data = self.trader.quotes_data(symbols) quotes_data = {} for quote in raw_data: quotes_data[quote['symbol']] = quote['last_trade_price'] table = BeautifulTable() table.column_headers = [ "symbol", "current price", "quantity", "total equity" ] for position in positions['results']: quantity = int(float(position['quantity'])) symbol = self.get_symbol(position['instrument']) price = quotes_data[symbol] total_equity = float(price) * quantity table.append_row([symbol, price, quantity, total_equity]) print(table) def do_b(self, arg): 'Buy stock b <symbol> <quantity> <price>' parts = arg.split() if len(parts) == 3: symbol = parts[0] quantity = parts[1] price = float(parts[2]) stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_buy_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_s(self, arg): 'Sell stock s <symbol> <quantity> <price>' parts = arg.split() if len(parts) == 3: symbol = parts[0] quantity = parts[1] price = float(parts[2]) stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_sell_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_o(self, arg): 'List open orders' open_orders = self.trader.get_open_orders() if open_orders: table = BeautifulTable() table.column_headers = [ "symbol", "price", "quantity", "type", "id" ] for order in open_orders: table.append_row([ self.get_symbol(order['instrument']), order['price'], int(float(order['quantity'])), order['side'], order['id'], ]) print(table) else: print "No Open Orders" def do_c(self, arg): 'Cancel open order c <id>' order_id = arg.strip() try: self.trader.cancel_order(order_id) print "Done" except Exception as e: print "Error executing cancel" print e def do_q(self, arg): 'Get quote for stock q <symbol>' symbol = arg.strip() try: self.trader.print_quote(symbol) except: print "Error getting quote for:", symbol def do_bye(self, arg): open(self.instruments_cache_file, 'w').write(json.dumps(self.instruments_cache)) return True # ------ utils -------- def get_symbol(self, url): if not url in self.instruments_reverse_cache: self.add_instrument_from_url(url) return self.instruments_reverse_cache[url] def add_instrument_from_url(self, url): data = self.trader.get_url(url) symbol = data['symbol'] self.instruments_cache[symbol] = url self.instruments_reverse_cache[url] = symbol
class RobinhoodShell(cmd.Cmd): intro = 'Welcome to the Robinhood shell. Type help or ? to list commands.\n' prompt = '> ' # API Object trader = None # Cache file used to store instrument cache instruments_cache_file = 'instruments.data' # Maps Symbol to Instrument URL instruments_cache = {} # Maps URL to Symbol instruments_reverse_cache = {} # Cache file used to store instrument cache watchlist_file = 'watchlist.data' # List of stocks in watchlist watchlist = [] def __init__(self): cmd.Cmd.__init__(self) self.trader = Robinhood() self.trader.login(username=USERNAME, password=PASSWORD) try: data = open(self.instruments_cache_file).read() self.instruments_cache = json.loads(data) for k in self.instruments_cache: self.instruments_reverse_cache[self.instruments_cache[k]] = k except: pass try: data = open(self.watchlist_file).read() self.watchlist = json.loads(data) except: pass # nytime = parser.parse('2018-06-15T23:14:15Z').astimezone(to_zone) # from dateutil import parser # ----- basic commands ----- def do_l(self, arg): 'Lists current portfolio' portfolio = self.trader.portfolios() if portfolio['extended_hours_equity']: equity = float(portfolio['extended_hours_equity']) else: equity = float(portfolio['equity']) print 'Equity Value: %.2f' % equity previous_close = float(portfolio['adjusted_equity_previous_close']) change = equity - previous_close print '%s%.2f Today (%.2f%%)' % (('+' if change > 0 else ''), change, change / previous_close * 100.0) account_details = self.trader.get_account() if 'margin_balances' in account_details: print 'Buying Power:', account_details['margin_balances'][ 'unallocated_margin_cash'] # Load Stocks positions = self.trader.securities_owned() symbols = [ self.get_symbol(position['instrument']) for position in positions['results'] ] quotes_data = {} if len(symbols) > 0: raw_data = self.trader.quotes_data(symbols) for quote in raw_data: if quote['last_extended_hours_trade_price']: price = quote['last_extended_hours_trade_price'] else: price = quote['last_trade_price'] quotes_data[quote['symbol']] = price table = BeautifulTable() table.column_headers = [ "symbol", "current price", "quantity", "total equity", "cost basis", "p/l" ] for position in positions['results']: quantity = int(float(position['quantity'])) symbol = self.get_symbol(position['instrument']) price = quotes_data[symbol] total_equity = float(price) * quantity buy_price = float(position['average_buy_price']) p_l = total_equity - buy_price * quantity table.append_row( [symbol, price, quantity, total_equity, buy_price, p_l]) print "Stocks:" print(table) # Load Options option_positions = self.trader.options_owned() table = BeautifulTable() table.column_headers = [ "option", "price", "quantity", "equity", "cost basis", "p/l" ] for op in option_positions: quantity = float(op['quantity']) if quantity == 0: continue cost = float(op['average_price']) if op['type'] == 'short': quantity = -quantity cost = -cost instrument = op['option'] option_data = self.trader.session.get(instrument).json() expiration_date = option_data['expiration_date'] strike = float(option_data['strike_price']) type = option_data['type'] symbol = op[ 'chain_symbol'] + ' ' + expiration_date + ' ' + type + ' $' + str( strike) info = self.trader.get_option_marketdata(instrument) last_price = float(info['adjusted_mark_price']) total_equity = (100 * last_price) * quantity change = total_equity - (float(cost) * quantity) table.append_row( [symbol, last_price, quantity, total_equity, cost, change]) print "Options:" print(table) def do_w(self, arg): 'Show watchlist w \nAdd to watchlist w a <symbol> \nRemove from watchlist w r <symbol>' parts = arg.split() if len(parts) == 2: if parts[0] == 'a': self.watchlist.append(parts[1].strip()) if parts[0] == 'r': self.watchlist = [ r for r in self.watchlist if not r == parts[1].strip() ] print "Done" else: table = BeautifulTable() table.column_headers = ["symbol", "current price"] if len(self.watchlist) > 0: raw_data = self.trader.quotes_data(self.watchlist) quotes_data = {} for quote in raw_data: table.append_row( [quote['symbol'], quote['last_trade_price']]) print(table) else: print "Watchlist empty!" def do_b(self, arg): 'Buy stock b <symbol> <quantity> <price>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] quantity = parts[1] if len(parts) == 3: price = float(parts[2]) else: price = 0.0 stock_instrument = self.get_instrument(symbol) if not stock_instrument['url']: print "Stock not found" return res = self.trader.place_buy_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_s(self, arg): 'Sell stock s <symbol> <quantity> <?price>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] quantity = parts[1] if len(parts) == 3: price = float(parts[2]) else: price = 0.0 stock_instrument = self.get_instrument(symbol) if not stock_instrument['url']: print "Stock not found" return res = self.trader.place_sell_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_sl(self, arg): 'Setup stop loss on stock sl <symbol> <quantity> <price>' parts = arg.split() if len(parts) == 3: symbol = parts[0] quantity = parts[1] price = float(parts[2]) stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_stop_loss_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_o(self, arg): 'List open orders' open_orders = self.trader.get_open_orders() if open_orders: table = BeautifulTable() table.column_headers = [ "index", "symbol", "price", "quantity", "type", "id" ] index = 1 for order in open_orders: if order['trigger'] == 'stop': order_price = order['stop_price'] order_type = "stop loss" else: order_price = order['price'] order_type = order['side'] + " " + order['type'] table.append_row([ index, self.get_symbol(order['instrument']), order_price, int(float(order['quantity'])), order_type, order['id'], ]) index += 1 print(table) else: print "No Open Orders" def do_c(self, arg): 'Cancel open order c <index> or c <id>' order_id = arg.strip() order_index = -1 try: order_index = int(order_id) except: pass if order_index > 0: order_index = order_index - 1 open_orders = self.trader.get_open_orders() if order_index < len(open_orders): order_id = open_orders[order_index]['id'] else: print "Bad index" return try: self.trader.cancel_order(order_id) print "Done" except Exception as e: print "Error executing cancel" print e def do_ca(self, arg): 'Cancel all open orders' open_orders = self.trader.get_open_orders() for order in open_orders: try: self.trader.cancel_order(order['id']) except Exception as e: pass print "Done" def do_q(self, arg): 'Get quote for stock q <symbol> or option q <symbol> <call/put> <strike> <(optional) YYYY-mm-dd>' arg = arg.strip().split() try: symbol = arg[0] except: print "Please check arguments again. Format: " print "Stock: q <symbol>" print "Option: q <symbol> <call/put> <strike> <(optional) YYYY-mm-dd>" type = strike = expiry = None if len(arg) > 1: try: type = arg[1] strike = arg[2] except Exception as e: print "Please check arguments again. Format: " print "q <symbol> <call/put> <strike> <(optional) YYYY-mm-dd>" try: expiry = arg[3] except: expiry = None arg_dict = { 'symbol': symbol, 'type': type, 'expiration_dates': expiry, 'strike_price': strike, 'state': 'active', 'tradability': 'tradable' } quotes = self.trader.get_option_quote(arg_dict) table = BeautifulTable() table.column_headers = ['expiry', 'price'] for row in quotes: table.append_row(row) print table else: try: self.trader.print_quote(symbol) except: print "Error getting quote for:", symbol def do_bye(self, arg): open(self.instruments_cache_file, 'w').write(json.dumps(self.instruments_cache)) open(self.watchlist_file, 'w').write(json.dumps(self.watchlist)) return True # ------ utils -------- def get_symbol(self, url): if not url in self.instruments_reverse_cache: self.add_instrument_from_url(url) return self.instruments_reverse_cache[url] def get_instrument(self, symbol): if not symbol in self.instruments_cache: instruments = self.trader.instruments(symbol) for instrument in instruments: self.add_instrument(instrument['url'], instrument['symbol']) url = '' if symbol in self.instruments_cache: url = self.instruments_cache[symbol] return {'symbol': symbol, 'url': url} def add_instrument_from_url(self, url): data = self.trader.get_url(url) if 'symbol' in data: symbol = data['symbol'] else: types = {'call': 'C', 'put': 'P'} symbol = data['chain_symbol'] + ' ' + data[ 'expiration_date'] + ' ' + ''.join( types[data['type']].split('-')) + ' ' + str( float(data['strike_price'])) self.add_instrument(url, symbol) def add_instrument(self, url, symbol): self.instruments_cache[symbol] = url self.instruments_reverse_cache[url] = symbol
class Query: # __init__:Void # param email:String => Email of the Robinhood user. # param password:String => Password for the Robinhood user. def __init__(self, email, password): self.trader = Robinhood() self.trader.login(username=email, password=password) self.email = email self.password = password ## ## # Getters # ## ## # get_fundamentals_by_criteria:[String] # param price_range:(float, float) => High and low prices for the queried fundamentals. # returns List of symbols that fit the given criteria. def get_fundamentals_by_criteria(self, price_range=(0.00, sys.maxsize), tags=None): all_symbols = [] if tags is not None and tags is not []: if isinstance(tags, Enum): try: all_symbols = self.get_by_tag(tag) except Exception as e: pass else: for tag in tags: try: all_symbols += self.get_by_tag(tag) except Exception as e: pass else: all_symbols = [ instrument['symbol'] for instrument in self.trader.instruments_all() ] queried_fundamentals = [] for symbol in all_symbols: try: fundamentals = self.get_fundamentals(symbol) if fundamentals is not None and 'low' in fundamentals and 'high' in fundamentals and float( fundamentals['low'] or -1) >= price_range[0] and float( fundamentals['high'] or sys.maxsize + 1) <= price_range[1]: fundamentals['symbol'] = symbol queried_fundamentals.append(fundamentals) except Exception as e: continue return queried_fundamentals # get_symbols_by_criteria:[String] # param price_range:(float, float) => High and low prices for the queried symbols. # returns List of symbols that fit the given criteria. def get_symbols_by_criteria(self, price_range=(0.00, sys.maxsize), tags=None): queried_fundamentals = self.get_fundamentals_by_criteria( price_range, tags) queried_symbols = [ fundamentals['symbol'] for fundamentals in queried_fundamentals ] return queried_symbols # get_current_price:[String:String] # param symbol:String => String symbol of the instrument to return. # returns Float value of the current price of the stock with the given symbol. def get_current_price(self, symbol): return float(self.trader.quote_data(symbol)['last_trade_price']) # get_quote:[String:String] # param symbol:String => String symbol of the instrument to return. # returns Quote data for the instrument with the given symbol. def get_quote(self, symbol): return self.trader.quote_data(symbol) # get_quotes:[[String:String]] # param symbol:[String] => List of string symbols of the instrument to return. # returns Quote data for the instruments with the given symbols. def get_quotes(self, symbols): return self.trader.quotes_data(symbols) # get_instrument:[String:String] # param symbol:String => String symbol of the instrument. # returns The instrument with the given symbol. def get_instrument(self, symbol): return self.trader.instruments(symbol)[0] or None # stock_from_instrument_url:Dict[String:String] # param url:String => URL of instrument. # returns Stock dictionary from the url of the instrument. def stock_from_instrument_url(self, url): return self.trader.stock_from_instrument_url(url) # get_history:[[String:String]] # param symbol:String => String symbol of the instrument. # param interval:Span => Time in between each value. (default: DAY) # param span:Span => Range for the data to be returned. (default: YEAR) # param bounds:Span => The bounds to be included. (default: REGULAR) # returns Historical quote data for the instruments with the given symbols on a 5-minute, weekly interval. def get_history(self, symbol, interval=Span.DAY, span=Span.YEAR, bounds=Bounds.REGULAR): return self.trader.get_historical_quotes(symbol, interval.value, span.value, bounds.value) # get_news:[[String:String]] # param symbol:String => String symbol of the instrument. # returns News for the instrument with the given symbol. def get_news(self, symbol): return self.trader.get_news(symbol) # get_fundamentals:Dict[String:String] # param symbol:String => String symbol of the instrument. # returns Fundamentals for the instrument with the given symbol. def get_fundamentals(self, symbol): return self.trader.get_fundamentals(symbol) # get_fundamentals:[String:String] # param symbol:String => String symbol of the instrument. # param dates:Date => List of datetime.date objects. # param type:Option => Option.CALL or Option.PUT # returns Options for the given symbol within the listed dates for the given type. def get_options(self, symbol, dates, type): return self.trader.get_options( symbol, list(map(lambda date: date.isoFormat(), dates)), type.value) # get_market_data:[String:String] # param optionId:String => Option ID for the option to return. # returns Options for the given ID. def get_market_data(self, optionId): return self.trader.get_option_market_data(optionId) # get_by_tag:[String:String] # param tag:Tag => Type of tag to return the quotes by. # returns Quotes for the given tag. def get_by_tag(self, tag): return self.trader.get_tickers_by_tag(tag.value) # get_current_bid_price:Float # param symbol:String => String symbol of the quote. # returns The current bid price of the stock, as a float. def get_current_bid_price(self, symbol): return float(self.trader.get_quote(symbol)['bid_price']) or 0.0 ## ## # User Methods # ## ## # user_portfolio:[String:String] # returns Portfolio model for the logged in user. def user_portfolio(self): quotes = [] user_portfolio = self.user_stock_portfolio() for data in user_portfolio: symbol = data['symbol'] count = float(data['quantity']) quotes.append(Quote(symbol, count)) return Portfolio(self, quotes, 'User Portfolio') # user_stock_portfolio:[String:String] # TODO: Better documentation. # returns Stock perfolio for the user. def user_stock_portfolio(self): positions = self.trader.positions()['results'] or [] return list( map( lambda position: Utility.merge_dicts( position, self.trader.session.get(position['instrument'], timeout=15) .json()), positions)) # user_portfolio:[String:String] # returns Positions for the logged in user. def user_positions(self): return self.trader.positions() # user_dividends:[String:String] # returns Dividends for the logged in user. def user_dividends(self): return self.trader.dividends() # user_securities:[String:String] # returns Securities for the logged in user. def user_securities(self): return self.trader.securities_owned() # user_equity:[String:String] # returns Equity for the logged in user. def user_equity(self): return self.trader.equity() # user_equity_prev:[String:String] # returns Equity upon the previous close for the logged in user. def user_equity_prev(self): return self.trader.equity_previous_close() # user_equity_adj_prev:[String:String] # returns Adjusted equity upon the previous close for the logged in user. def user_equity_adj_prev(self): return self.trader.adjusted_equity_previous_close() # user_equity_ext_hours:[String:String] # returns Extended hours equity for the logged in user. def user_equity_ext_hours(self): return self.trader.extended_hours_equity() # user_equity_last_core:[String:String] # returns Last core equity for the logged in user. def user_equity_last_core(self): return self.trader.last_core_equity() # user_excess_margin:[String:String] # returns Excess margin for the logged in user. def user_excess_margin(self): return self.trader.excess_margin() # user_market_value:[String:String] # returns Market value for the logged in user. def user_market_value(self): return self.trader.market_value() # user_market_value_ext_hours:[String:String] # returns Extended hours market value for the logged in user. def user_market_value_ext_hours(self): return self.trader.extended_hours_market_value() # user_market_value_last_core:[String:String] # returns Last core market value for the logged in user. def user_market_value_last_core(self): return self.trader.last_core_market_value() # user_order_history:[String:String] # param orderId:String => The order ID to return the order for. # returns A specified order executed by the logged in user. def user_order(self, orderId): return self.trader.order_history(orderId) # user_orders:[[String:String]] # returns The order history for the logged in user. def user_orders(self): return self.trader.order_history(None) # user_open_orders:[[String:String]] # returns The open orders for the user def user_open_orders(self): orders = self.trader.order_history(None)['results'] open_orders = [] for order in orders: if order['state'] == 'queued': open_orders.append(order) return open_orders # user_account:[[String:String]] # returns The user's account. def user_account(self): return self.trader.get_account() # user_buying_power:float # returns The user's buying power. def user_buying_power(self): return float(self.trader.get_account()['buying_power'] or 0.0) ## ## # Execution Methods # ## ## # exec_buy:[String:String] # param symbol:String => String symbol of the instrument. # param quantity:Number => Number of shares to execute buy for. # param stop:Number? => Sets a stop price on the buy, if not None. # param limit:Number? => Sets a limit price on the buy, if not None. # param time:GoodFor? => Defines the expiration for a limited buy. # returns The order response. def exec_buy(self, symbol, quantity, stop=None, limit=None, time=None): if time is None: time = GoodFor.GOOD_TIL_CANCELED if limit is not None: if stop is not None: return self.trader.place_stop_limit_buy_order( None, symbol, time.value, stop, quantity) return self.trader.place_limit_buy_order(None, symbol, time.value, limit, quantity) elif stop is not None: return self.trader.place_stop_loss_buy_order( None, symbol, time.value, stop, quantity) return self.trader.place_market_buy_order(None, symbol, time.value, quantity) # exec_sell:[String:String] # param symbol:String => String symbol of the instrument. # param quantity:Number => Number of shares to execute sell for. # param stop:Number? => Sets a stop price on the sell, if not None. # param limit:Number? => Sets a limit price on the sell, if not None. # param time:GoodFor? => Defines the expiration for a limited buy. # returns The order response. def exec_sell(self, symbol, quantity, stop=None, limit=None, time=None): if time is None: time = GoodFor.GOOD_TIL_CANCELED if limit is not None: if stop is not None: return self.trader.place_stop_limit_sell_order( None, symbol, time.value, stop, quantity) return self.trader.place_limit_sell_order(None, symbol, time.value, limit, quantity) elif stop is not None: return self.trader.place_stop_loss_sell_order( None, symbol, time.value, stop, quantity) return self.trader.place_market_sell_order(None, symbol, time.value, quantity) # exec_cancel:[String:String] # param order_id:String => ID of the order to cancel. # returns The canceled order response. def exec_cancel(self, order_id): return self.trader.cancel_order(order_id) # exec_cancel_open_orders:[String] # returns A list of string IDs for the cancelled orders. def exec_cancel_open_orders(self): orders = self.trader.order_history(None)['results'] cancelled_order_ids = [] for order in orders: if order['state'] == 'queued': self.trader.cancel_order(order['id']) cancelled_order_ids.append(order['id']) return cancelled_order_ids
class RobinhoodShell(cmd.Cmd): intro = 'Welcome to the Robinhood shell. Type help or ? to list commands.\n' prompt = '> ' # API Object trader = None # Cache file used to store instrument cache instruments_cache_file = 'instruments.data' # Maps Symbol to Instrument URL instruments_cache = {} # Maps URL to Symbol instruments_reverse_cache = {} # Cache file used to store instrument cache watchlist_file = 'watchlist.data' # List of stocks in watchlist watchlist = [] def __init__(self): cmd.Cmd.__init__(self) self.trader = Robinhood() self.trader.login(username=USERNAME, password=PASSWORD) try: data = open(self.instruments_cache_file).read() self.instruments_cache = json.loads(data) for k in self.instruments_cache: self.instruments_reverse_cache[self.instruments_cache[k]] = k except: pass try: data = open(self.watchlist_file).read() self.watchlist = json.loads(data) except: pass # nytime = parser.parse('2018-06-15T23:14:15Z').astimezone(to_zone) # from dateutil import parser # ----- basic commands ----- def do_l(self, arg): 'Lists current portfolio' t = Terminal() portfolio = self.trader.portfolios() if portfolio['extended_hours_equity']: equity = float(portfolio['extended_hours_equity']) else: equity = float(portfolio['equity']) eq = '%.2f' % equity previous_close = float(portfolio['adjusted_equity_previous_close']) change = equity - previous_close change_pct = '%.2f' % (change/previous_close * 100.0) change_pct = color_data(change_pct) change = color_data(change) account_details = self.trader.get_account() if 'margin_balances' in account_details: buying_power = account_details['margin_balances']['unallocated_margin_cash'] account_table = SingleTable([['Portfolio Value','Change','Buying Power'],[eq, change+' ('+change_pct+'%)', buying_power]],'Account') print(account_table.table) # Load Stocks positions = self.trader.securities_owned() symbols = [self.get_symbol(position['instrument']) for position in positions['results']] quotes_data = {} if len(symbols) > 0: raw_data = self.trader.quotes_data(symbols) for quote in raw_data: quotes_data[quote['symbol']] = quote if quote['last_extended_hours_trade_price']: price = quote['last_extended_hours_trade_price'] else: price = quote['last_trade_price'] table_data = [] table_data.append(["Symbol", "Last", "Shares", "Equity", "Avg Cost", "Return" , "Day", "EquityChange", "Day %"]) for position in positions['results']: quantity = int(float(position['quantity'])) symbol = self.get_symbol(position['instrument']) price = quotes_data[symbol]['last_trade_price'] total_equity = float(price) * quantity buy_price = float(position['average_buy_price']) p_l = total_equity - buy_price * quantity day_change = float(quotes_data[symbol]['last_trade_price']) - float(quotes_data[symbol]['previous_close']) day_change_q_val = '{:04.2f}'.format(quantity * day_change) day_change_pct = '{:04.2f}'.format(float( ( day_change / float(quotes_data[symbol]['previous_close']) ) * 100)) table_data.append([ symbol, price, quantity, total_equity, buy_price, color_data(p_l), color_data(day_change), color_data(day_change_q_val), color_data(day_change_pct) ]) table = SingleTable(table_data,'Portfolio') table.inner_row_border = True table.justify_columns = {0: 'center' } print table.table def do_lo(self, arg): 'Lists current options portfolio' # Load Options options_t_data=[] option_positions = self.trader.options_owned() options_table = SingleTable(options_t_data,'Options') options_table.inner_row_border = True options_table.justify_columns = {0: 'center' } options_t_data.append(["Symbol","Type","Experation","Strike", "Price", "QTY", "Equity", "Cost", "Total Return","Today"]) for op in option_positions: quantity = float(op['quantity']) if quantity == 0: continue cost = float(op['average_price']) if op['type'] == 'short': quantity = -quantity cost = -cost instrument = op['option'] option_data = self.trader.session.get(instrument).json() # skip expired -- verify when it changes state day of or, after market close on expieration if option_data['state'] == "expired": continue expiration_date = option_data['expiration_date'] strike = float(option_data['strike_price']) type = option_data['type'] symbol = op['chain_symbol'] option_type = str(type).upper() expiration = expiration_date strike_price = '$'+str(strike) info = self.trader.get_option_marketdata(instrument) last_price = float(info['adjusted_mark_price']) total_equity = (100 * last_price) * quantity change = total_equity - (float(cost) * quantity) change_pct = '{:04.2f}'.format(change / float(cost) * 100) day_change = float(info['adjusted_mark_price']) - float(info['previous_close_price']) day_pct = '{:04.2f}'.format((day_change / float(info['previous_close_price']) ) * 100) options_t_data.append([ symbol,option_type, expiration, strike_price , last_price, quantity, total_equity, cost, color_data(change) +' ('+ color_data(change_pct) +'%)', color_data(day_change) +' ('+ color_data(day_pct) +'%)' ]) print(options_table.table) def do_w(self, arg): 'Show watchlist w \nAdd to watchlist w a <symbol> \nRemove from watchlist w r <symbol>' parts = arg.split() if len(parts) == 2: if parts[0] == 'a': self.watchlist.append(parts[1].strip()) if parts[0] == 'r': self.watchlist = [r for r in self.watchlist if not r == parts[1].strip()] print "Done" else: watch_t_data=[] watch_table = SingleTable(watch_t_data,'Watch List') watch_table.inner_row_border = True watch_table.justify_columns = {0: 'center', 1: 'center', 2: 'center', 3:'center',4: 'center'} watch_t_data.append(["Symbol","Ask Price", "Open", "Today", "%"]) if len(self.watchlist) > 0: raw_data = self.trader.quotes_data(self.watchlist) quotes_data = {} for quote in raw_data: day_change = float(quote['last_trade_price']) - float(quote['previous_close']) day_change_pct = '{:05.2f}'.format(( day_change / float(quote['previous_close']) ) * 100) watch_t_data.append([ quote['symbol'], '{:05.2f}'.format(float(quote['last_trade_price'])), '{:05.2f}'.format(float(quote['previous_close'])), color_data(day_change), color_data(day_change_pct) ]) print(watch_table.table) else: print "Watchlist empty!" def do_b(self, arg): 'Buy stock b <symbol> <quantity> <price>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] quantity = parts[1] if len(parts) == 3: price = float(parts[2]) else: price = 0.0 stock_instrument = self.get_instrument(symbol) if not stock_instrument['url']: print "Stock not found" return res = self.trader.place_buy_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_s(self, arg): 'Sell stock s <symbol> <quantity> <?price>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] quantity = parts[1] if len(parts) == 3: price = float(parts[2]) else: price = 0.0 stock_instrument = self.get_instrument(symbol) if not stock_instrument['url']: print "Stock not found" return res = self.trader.place_sell_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_sl(self, arg): 'Setup stop loss on stock sl <symbol> <quantity> <price>' parts = arg.split() if len(parts) == 3: symbol = parts[0] quantity = parts[1] price = float(parts[2]) stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_stop_loss_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_o(self, arg): 'List open orders' open_orders = self.trader.get_open_orders() if open_orders: open_t_data=[] open_table = SingleTable(open_t_data,'open List') open_table.inner_row_border = True open_table.justify_columns = {0: 'center', 1: 'center', 2: 'center', 3:'center',4: 'center'} open_t_data.append( ["index", "symbol", "price", "quantity", "type", "id"]) index = 1 for order in open_orders: if order['trigger'] == 'stop': order_price = order['stop_price'] order_type = "stop loss" else: order_price = order['price'] order_type = order['side']+" "+order['type'] open_t_data.append([ index, self.get_symbol(order['instrument']), order_price, int(float(order['quantity'])), order_type, order['id'], ]) index += 1 print(open_table.table) else: print "No Open Orders" def do_c(self, arg): 'Cancel open order c <index> or c <id>' order_id = arg.strip() order_index = -1 try: order_index = int(order_id) except: pass if order_index > 0: order_index = order_index - 1 open_orders = self.trader.get_open_orders() if order_index < len(open_orders): order_id = open_orders[order_index]['id'] else: print "Bad index" return try: self.trader.cancel_order(order_id) print "Done" except Exception as e: print "Error executing cancel" print e def do_ca(self, arg): 'Cancel all open orders' open_orders = self.trader.get_open_orders() for order in open_orders: try: self.trader.cancel_order(order['id']) except Exception as e: pass print "Done" def do_news(self,arg,show_num=5): if len(arg) == 0: print "Missing symbol" else: news_data = self.trader.get_news(arg.upper()) if news_data['count'] == 0: print("No News available") return for x in range(0,show_num): news_box(news_data['results'][x]['source'], news_data['results'][x]['published_at'], news_data['results'][x]['summary'], news_data['results'][x]['title'],news_data['results'][x]['url']) # print("-----------------------------------------------------------------------------------") # print("Source: "+news_data['results'][x]['source']+': '+news_data['results'][x]['published_at']+' - '+news_data['results'][x]['title']) # print("URL: "+news_data['results'][x]['url']) # print("Summary: "+news_data['results'][x]['summary']) def do_mp(self, arg): 'Buy as many shares possible by defined max dollar amount: mp <symbol> <max_spend> <?price_limit>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] #quantity = parts[1] spend = parts[1] if len(parts) == 3: print "Parts: 3" price_limit = float(parts[2]) else: price_limit = 0.0 try: cur_data = self.trader.quote_data(symbol) last_price = cur_data['last_trade_price'] except: print "Invalid Ticker?" pass return # quote['last_trade_price'] quantity = int(math.floor(float(spend) / float(last_price))) print("\nBuying %s\n Max Spend: %s\nQTY: %s\n Current Price: %s\nMax Price: %s\n" % (symbol,spend, quantity, last_price, price_limit)) # stock_instrument = self.trader.instruments(symbol)[0] # res = self.trader.place_buy_order(stock_instrument, quantity, price) # if not (res.status_code == 200 or res.status_code == 201): # print "Error executing order" # try: # data = res.json() # if 'detail' in data: # print data['detail'] # except: # pass # else: # print "Done" # else: # print "Bad Order" def do_q(self, arg): 'Get detailed quote for stock: q <symblol(s)>' symbols = re.split('\W+',arg) if len(arg) == 0: print "Missing symbol(s)" else: raw_data = self.trader.quotes_data(symbols) quotes_data = {} quote_t_data=[] quote_table = SingleTable(quote_t_data,'Quote List') quote_table.inner_row_border = True quote_table.justify_columns = {0: 'center', 1: 'center', 2: 'center', 3:'center',4: 'center'} quote_t_data.append(["Symbol", "Current Price", "Open","Change", "Ask","Bid"]) for quote in raw_data: if not quote: continue day_change = float(quote['last_trade_price']) - float(quote['previous_close']) day_change_pct = ( day_change / float(quote['previous_close']) ) * 100 ask_price = '{:05.2f}'.format(float(quote['ask_price'])) ask_size = quote['ask_size'] bid_price = '{:05.2f}'.format(float(quote['bid_price'])) bid_size = quote['bid_size'] quote_t_data.append([ quote['symbol'], '{:05.2f}'.format(float(quote['last_trade_price'])), '{:05.2f}'.format(float(quote['previous_close'])), color_data(day_change)+' ('+color_data('{:05.2f}'.format(day_change_pct))+'%)', str(ask_price)+' x '+str(ask_size), str(bid_price)+' x '+str(bid_size) ]) print(quote_table.table) def do_qq(self, arg): 'Get quote for stock q <symbol> or option q <symbol> <call/put> <strike> <(optional) YYYY-mm-dd>' arg = arg.strip().split() try: symbol = arg[0]; except: print "Please check arguments again. Format: " print "Stock: q <symbol>" print "Option: q <symbol> <call/put> <strike> <(optional) YYYY-mm-dd>" type = strike = expiry = None if len(arg) > 1: try: type = arg[1] strike = arg[2] except Exception as e: print "Please check arguments again. Format: " print "q <symbol> <call/put> <strike> <(optional) YYYY-mm-dd>" try: expiry = arg[3] except: expiry = None arg_dict = {'symbol': symbol, 'type': type, 'expiration_dates': expiry, 'strike_price': strike, 'state': 'active', 'tradability': 'tradable'}; quotes = self.trader.get_option_quote(arg_dict); qquote_t_data=[] qquote_table = SingleTable(qquote_t_data,'Quote List') qquote_table.inner_row_border = True qquote_table.justify_columns = {0: 'center', 1: 'center'} qquote_t_data.append(['expiry', 'price']) for row in quotes: qquote_t_data.append(row) print(qquote_table.table) else: try: self.trader.print_quote(symbol) except: print "Error getting quote for:", symbol def do_bye(self, arg): open(self.instruments_cache_file, 'w').write(json.dumps(self.instruments_cache)) open(self.watchlist_file, 'w').write(json.dumps(self.watchlist)) return True # ------ utils -------- def get_symbol(self, url): if not url in self.instruments_reverse_cache: self.add_instrument_from_url(url) return self.instruments_reverse_cache[url] def get_instrument(self, symbol): if not symbol in self.instruments_cache: instruments = self.trader.instruments(symbol) for instrument in instruments: self.add_instrument(instrument['url'], instrument['symbol']) url = '' if symbol in self.instruments_cache: url = self.instruments_cache[symbol] return { 'symbol': symbol, 'url': url } def add_instrument_from_url(self, url): data = self.trader.get_url(url) if 'symbol' in data: symbol = data['symbol'] else: types = { 'call': 'C', 'put': 'P'} symbol = data['chain_symbol'] + ' ' + data['expiration_date'] + ' ' + ''.join(types[data['type']].split('-')) + ' ' + str(float(data['strike_price'])) self.add_instrument(url, symbol) def add_instrument(self, url, symbol): self.instruments_cache[symbol] = url self.instruments_reverse_cache[url] = symbol
class RobinhoodShell(cmd.Cmd): intro = 'Welcome to the Robinhood shell. Type help or ? to list commands.\n' prompt = '> ' # API Object trader = None # Cache file used to store instrument cache instruments_cache_file = 'instruments.data' # Maps Symbol to Instrument URL instruments_cache = {} # Maps URL to Symbol instruments_reverse_cache = {} # Cache file used to store instrument cache watchlist_file = 'watchlist.data' # List of stocks in watchlist watchlist = [] def __init__(self): cmd.Cmd.__init__(self) self.trader = Robinhood() self.trader.login(username=USERNAME, password=PASSWORD) try: data = open(self.instruments_cache_file).read() self.instruments_cache = json.loads(data) for k in self.instruments_cache: self.instruments_reverse_cache[self.instruments_cache[k]] = k except: pass try: data = open(self.watchlist_file).read() self.watchlist = json.loads(data) except: pass # ----- basic commands ----- def do_l(self, arg): 'Lists current portfolio' portfolio = self.trader.portfolios() print 'Equity Value:', portfolio['equity'] account_details = self.trader.get_account() if 'margin_balances' in account_details: print 'Buying Power:', account_details['margin_balances']['unallocated_margin_cash'] positions = self.trader.securities_owned() symbols = [] buy_price_data = {} for position in positions['results']: symbol = self.get_symbol(position['instrument']) buy_price_data[symbol] = position['average_buy_price'] symbols.append(symbol) quotes_data = {} if len(symbols) > 0: raw_data = self.trader.quotes_data(symbols) for quote in raw_data: quotes_data[quote['symbol']] = quote['last_trade_price'] table = BeautifulTable() table.column_headers = ["symbol", "current price", "quantity", "total equity", "cost basis", "p/l"] for position in positions['results']: quantity = int(float(position['quantity'])) symbol = self.get_symbol(position['instrument']) price = quotes_data[symbol] total_equity = float(price) * quantity buy_price = float(buy_price_data[symbol]) p_l = total_equity - buy_price * quantity table.append_row([symbol, price, quantity, total_equity, buy_price, p_l]) print(table) def do_w(self, arg): 'Show watchlist w \nAdd to watchlist w a <symbol> \nRemove from watchlist w r <symbol>' parts = arg.split() if len(parts) == 2: if parts[0] == 'a': self.watchlist.append(parts[1].strip()) if parts[0] == 'r': self.watchlist = [r for r in self.watchlist if not r == parts[1].strip()] print "Done" else: table = BeautifulTable() table.column_headers = ["symbol", "current price"] if len(self.watchlist) > 0: raw_data = self.trader.quotes_data(self.watchlist) quotes_data = {} for quote in raw_data: table.append_row([quote['symbol'], quote['last_trade_price']]) print(table) else: print "Watchlist empty!" def do_b(self, arg): 'Buy stock b <symbol> <quantity> <price>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] quantity = parts[1] if len(parts) == 3: price = float(parts[2]) else: price = 0.0 stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_buy_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_s(self, arg): 'Sell stock s <symbol> <quantity> <?price>' parts = arg.split() if len(parts) >= 2 and len(parts) <= 3: symbol = parts[0] quantity = parts[1] if len(parts) == 3: price = float(parts[2]) else: price = 0.0 stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_sell_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_sl(self, arg): 'Setup stop loss on stock sl <symbol> <quantity> <price>' parts = arg.split() if len(parts) == 3: symbol = parts[0] quantity = parts[1] price = float(parts[2]) stock_instrument = self.trader.instruments(symbol)[0] res = self.trader.place_stop_loss_order(stock_instrument, quantity, price) if not (res.status_code == 200 or res.status_code == 201): print "Error executing order" try: data = res.json() if 'detail' in data: print data['detail'] except: pass else: print "Done" else: print "Bad Order" def do_o(self, arg): 'List open orders' open_orders = self.trader.get_open_orders() if open_orders: table = BeautifulTable() table.column_headers = ["index", "symbol", "price", "quantity", "type", "id"] index = 1 for order in open_orders: table.append_row([ index, self.get_symbol(order['instrument']), order['price'], int(float(order['quantity'])), order['side'], order['id'], ]) index += 1 print(table) else: print "No Open Orders" def do_c(self, arg): 'Cancel open order c <index> or c <id>' order_id = arg.strip() order_index = -1 try: order_index = int(order_id) except: pass if order_index > 0: order_index = order_index - 1 open_orders = self.trader.get_open_orders() if order_index < len(open_orders): order_id = open_orders[order_index]['id'] else: print "Bad index" return try: self.trader.cancel_order(order_id) print "Done" except Exception as e: print "Error executing cancel" print e def do_ca(self, arg): 'Cancel all open orders' open_orders = self.trader.get_open_orders() for order in open_orders: try: self.trader.cancel_order(order['id']) except Exception as e: pass print "Done" def do_q(self, arg): 'Get quote for stock q <symbol>' symbol = arg.strip() try: self.trader.print_quote(symbol) except: print "Error getting quote for:", symbol def do_bye(self, arg): open(self.instruments_cache_file, 'w').write(json.dumps(self.instruments_cache)) open(self.watchlist_file, 'w').write(json.dumps(self.watchlist)) return True # ------ utils -------- def get_symbol(self, url): if not url in self.instruments_reverse_cache: self.add_instrument_from_url(url) return self.instruments_reverse_cache[url] def add_instrument_from_url(self, url): data = self.trader.get_url(url) symbol = data['symbol'] self.instruments_cache[symbol] = url self.instruments_reverse_cache[url] = symbol