From 79704f37f60721b7155e9339b14c674be04ae9a0 Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Thu, 26 Oct 2017 22:08:33 +0200 Subject: [PATCH 1/8] Uniform + consistent + spacious: documentation, follows most PEP 8 stands --- Robinhood/Robinhood.py | 697 ++++++++++++++++++++++++----------------- 1 file changed, 407 insertions(+), 290 deletions(-) diff --git a/Robinhood/Robinhood.py b/Robinhood/Robinhood.py index 9e7d3075..74169f92 100644 --- a/Robinhood/Robinhood.py +++ b/Robinhood/Robinhood.py @@ -1,30 +1,42 @@ -"""Robinhood.py: a collection of utilities for working with Robinhood's Private API""" -import getpass -import logging -import warnings -from enum import Enum +""" + Robinhood.py: a collection of utilities for working with Robinhood's Private API +""" -import requests -import six +from enum import Enum from six.moves.urllib.parse import unquote from six.moves.urllib.request import getproxies from six.moves import input from . import exceptions as RH_exception +import getpass +import requests +import six + +import logging +import warnings + + class Bounds(Enum): - """enum for bounds in `historicals` endpoint""" + """ + Enum for bounds in `historicals` endpoint + """ REGULAR = 'regular' EXTENDED = 'extended' class Transaction(Enum): - """enum for buy/sell orders""" + """ + Enum for buy/sell orders + """ BUY = 'buy' SELL = 'sell' class Robinhood: - """wrapper class for fetching/parsing Robinhood endpoints""" + """ + Wrapper class for fetching/parsing Robinhood endpoints + """ + endpoints = { "login": "https://api.robinhood.com/api-token-auth/", "logout": "https://api.robinhood.com/api-token-logout/", @@ -66,9 +78,10 @@ class Robinhood: logger = logging.getLogger('Robinhood') logger.addHandler(logging.NullHandler()) - ############################## - #Logging in and initializing - ############################## + + ########################################################################### + # Logging in and initializing + ########################################################################### def __init__(self): self.session = requests.session() @@ -90,13 +103,9 @@ def login_prompt(self): #pragma: no cover password = getpass.getpass() return self.login(username=username, password=password) - def login( - self, - username, - password, - mfa_code=None - ): - """save and test login info for Robinhood accounts + def login(self, username, password, mfa_code=None): + """ + Save and test login info for Robinhood accounts Args: username (str): username @@ -106,6 +115,7 @@ def login( (bool): received valid auth token """ + self.username = username self.password = password payload = { @@ -117,10 +127,7 @@ def login( payload['mfa_code'] = mfa_code try: - res = self.session.post( - self.endpoints['login'], - data=payload - ) + res = self.session.post(self.endpoints['login'], data=payload) res.raise_for_status() data = res.json() except requests.exceptions.HTTPError: @@ -136,13 +143,16 @@ def login( return False + def logout(self): - """logout from Robinhood + """ + Sogout from Robinhood Returns: (:obj:`requests.request`) result from logout endpoint """ + try: req = self.session.post(self.endpoints['logout']) req.raise_for_status() @@ -154,30 +164,34 @@ def logout(self): return req - ############################## - #GET DATA - ############################## + ########################################################################### + # GET DATA + ########################################################################### def investment_profile(self): - """fetch investment_profile""" + """ + Fetch investment_profile + """ + res = self.session.get(self.endpoints['investment_profile']) res.raise_for_status() #will throw without auth data = res.json() return data - def instruments(self, stock): - """fetch instruments endpoint - Args: - stock (str): stock ticker + def instruments(self, stock): + """ + Fetch instruments endpoint - Returns: - (:obj:`dict`): JSON contents from `instruments` endpoint + Args: + stock (str): stock ticker + Returns: + (:obj:`dict`): JSON contents from `instruments` endpoint """ - res = self.session.get( - self.endpoints['instruments'], - params={'query': stock.upper()} + + res = self.session.get(self.endpoints['instruments'], + params={'query': stock.upper()} ) res.raise_for_status() res = res.json() @@ -188,21 +202,24 @@ def instruments(self, stock): return res['results'] - def quote_data(self, stock=''): - """fetch stock quote - Args: - stock (str): stock ticker, prompt if blank + def quote_data(self, stock=''): + """ + Fetch stock quote - Returns: - (:obj:`dict`): JSON contents from `quotes` endpoint + Args: + stock (str): stock ticker, prompt if blank + Returns: + (:obj:`dict`): JSON contents from `quotes` endpoint """ + url = None if stock.find(',') == -1: url = str(self.endpoints['quotes']) + str(stock) + "/" else: url = str(self.endpoints['quotes']) + "?symbols=" + str(stock) + #Check for validity of symbol try: req = requests.get(url) @@ -213,17 +230,20 @@ def quote_data(self, stock=''): return data + # We will keep for compatibility until next major release def quotes_data(self, stocks): - """Fetch quote for multiple stocks, in one single Robinhood API call + """ + Fetch quote for multiple stocks, in one single Robinhood API call - Args: - stocks (list): stock tickers + Args: + stocks (list): stock tickers - Returns: - (:obj:`list` of :obj:`dict`): List of JSON contents from `quotes` endpoint, in the - same order of input args. If any ticker is invalid, a None will occur at that position. + Returns: + (:obj:`list` of :obj:`dict`): List of JSON contents from `quotes` endpoint, in the + same order of input args. If any ticker is invalid, a None will occur at that position. """ + url = str(self.endpoints['quotes']) + "?symbols=" + ",".join(stocks) try: req = requests.get(url) @@ -234,19 +254,22 @@ def quotes_data(self, stocks): return data["results"] + def get_quote_list(self, stock='', key=''): - """Returns multiple stock info and keys from quote_data (prompt if blank) + """ + Returns multiple stock info and keys from quote_data (prompt if blank) - Args: - stock (str): stock ticker (or tickers separated by a comma) - , prompt if blank - key (str): key attributes that the function should return + Args: + stock (str): stock ticker (or tickers separated by a comma) + , prompt if blank + key (str): key attributes that the function should return - Returns: - (:obj:`list`): Returns values from each stock or empty list - if none of the stocks were valid + Returns: + (:obj:`list`): Returns values from each stock or empty list + if none of the stocks were valid """ + #Creates a tuple containing the information we want to retrieve def append_stock(stock): keys = key.split(',') @@ -254,51 +277,56 @@ def append_stock(stock): for item in keys: myStr += stock[item] + "," return (myStr.split(',')) + #Prompt for stock if not entered if not stock: #pragma: no cover stock = input("Symbol: ") + data = self.quote_data(stock) res = [] + # Handles the case of multple tickers if stock.find(',') != -1: for stock in data['results']: - if stock == None: + if stock is None: continue res.append(append_stock(stock)) + else: res.append(append_stock(data)) + return res + def get_quote(self, stock=''): - """wrapper for quote_data""" + """ + Wrapper for quote_data + """ + data = self.quote_data(stock) return data["symbol"] - def get_historical_quotes( - self, - stock, - interval, - span, - bounds=Bounds.REGULAR - ): - """fetch historical data for stock - Note: valid interval/span configs - interval = 5minute | 10minute + span = day, week - interval = day + span = year - interval = week - TODO: NEEDS TESTS + def get_historical_quotes(self, stock, interval, span, bounds=Bounds.REGULAR): + """ + Fetch historical data for stock - Args: - stock (str): stock ticker - interval (str): resolution of data - span (str): length of data - bounds (:enum:`Bounds`, optional): 'extended' or 'regular' trading hours + Note: valid interval/span configs + interval = 5minute | 10minute + span = day, week + interval = day + span = year + interval = week + TODO: NEEDS TESTS - Returns: - (:obj:`dict`) values returned from `historicals` endpoint + Args: + stock (str): stock ticker + interval (str): resolution of data + span (str): length of data + bounds (:enum:`Bounds`, optional): 'extended' or 'regular' trading hours + Returns: + (:obj:`dict`) values returned from `historicals` endpoint """ + if isinstance(bounds, str): #recast to Enum bounds = Bounds(bounds) @@ -308,237 +336,272 @@ def get_historical_quotes( 'span': span, 'bounds': bounds.name.lower() } - res = self.session.get( - self.endpoints['historicals'], - params=params - ) + + res = self.session.get(self.endpoints['historicals'], params=params) return res.json() - def get_news(self, stock): - """fetch news endpoint - Args: - stock (str): stock ticker - Returns: - (:obj:`dict`) values returned from `news` endpoint + def get_news(self, stock): + """ + Fetch news endpoint + Args: + stock (str): stock ticker + Returns: + (:obj:`dict`) values returned from `news` endpoint """ + return self.session.get(self.endpoints['news']+stock.upper()+"/").json() - def print_quote(self, stock=''): #pragma: no cover - """print quote information - Args: - stock (str): ticker to fetch - Returns: - None + def print_quote(self, stock=''): #pragma: no cover + """ + Print quote information + Args: + stock (str): ticker to fetch + Returns: + None """ + data = self.get_quote_list(stock,'symbol,last_trade_price') for item in data: quote_str = item[0] + ": $" + item[1] print(quote_str) self.logger.info(quote_str) - def print_quotes(self, stocks): #pragma: no cover - """print a collection of stocks - Args: - stocks (:obj:`list`): list of stocks to pirnt + def print_quotes(self, stocks): #pragma: no cover + """ + Print a collection of stocks - Returns: - None + Args: + stocks (:obj:`list`): list of stocks to pirnt + Returns: + None """ + + if stocks is None: + return + for stock in stocks: self.print_quote(stock) + def ask_price(self, stock=''): - """get asking price for a stock + """ + Get asking price for a stock - Note: - queries `quote` endpoint, dict wrapper + Note: + queries `quote` endpoint, dict wrapper - Args: - stock (str): stock ticker - - Returns: - (float): ask price + Args: + stock (str): stock ticker + Returns: + (float): ask price """ + return self.get_quote_list(stock,'ask_price') - def ask_size(self, stock=''): - """get ask size for a stock - Note: - queries `quote` endpoint, dict wrapper + def ask_size(self, stock=''): + """ + Get ask size for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (int): ask size + Args: + stock (str): stock ticker + Returns: + (int): ask size """ + return self.get_quote_list(stock,'ask_size') - def bid_price(self, stock=''): - """get bid price for a stock - Note: - queries `quote` endpoint, dict wrapper + def bid_price(self, stock=''): + """ + Get bid price for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (float): bid price + Args: + stock (str): stock ticker + Returns: + (float): bid price """ + return self.get_quote_list(stock,'bid_price') - def bid_size(self, stock=''): - """get bid size for a stock - Note: - queries `quote` endpoint, dict wrapper + def bid_size(self, stock=''): + """ + Get bid size for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (int): bid size + Args: + stock (str): stock ticker + Returns: + (int): bid size """ + return self.get_quote_list(stock,'bid_size') - def last_trade_price(self, stock=''): - """get last trade price for a stock - Note: - queries `quote` endpoint, dict wrapper + def last_trade_price(self, stock=''): + """ + Get last trade price for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (float): last trade price + Args: + stock (str): stock ticker + Returns: + (float): last trade price """ + return self.get_quote_list(stock,'last_trade_price') - def previous_close(self, stock=''): - """get previous closing price for a stock - Note: - queries `quote` endpoint, dict wrapper + def previous_close(self, stock=''): + """ + Get previous closing price for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (float): previous closing price + Args: + stock (str): stock ticker + Returns: + (float): previous closing price """ + return self.get_quote_list(stock,'previous_close') - def previous_close_date(self, stock=''): - """get previous closing date for a stock - Note: - queries `quote` endpoint, dict wrapper + def previous_close_date(self, stock=''): + """ + Get previous closing date for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (str): previous close date + Args: + stock (str): stock ticker + Returns: + (str): previous close date """ + return self.get_quote_list(stock,'previous_close_date') - def adjusted_previous_close(self, stock=''): - """get adjusted previous closing price for a stock - Note: - queries `quote` endpoint, dict wrapper + def adjusted_previous_close(self, stock=''): + """ + Get adjusted previous closing price for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (float): adjusted previous closing price + Args: + stock (str): stock ticker + Returns: + (float): adjusted previous closing price """ + return self.get_quote_list(stock,'adjusted_previous_close') - def symbol(self, stock=''): - """get symbol for a stock - Note: - queries `quote` endpoint, dict wrapper + def symbol(self, stock=''): + """ + Get symbol for a stock - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (str): stock symbol + Args: + stock (str): stock ticker + Returns: + (str): stock symbol """ + return self.get_quote_list(stock,'symbol') - def last_updated_at(self, stock=''): - """get last update datetime - Note: - queries `quote` endpoint, dict wrapper + def last_updated_at(self, stock=''): + """ + Get last update datetime - Args: - stock (str): stock ticker + Note: + queries `quote` endpoint, dict wrapper - Returns: - (str): last update datetime + Args: + stock (str): stock ticker + Returns: + (str): last update datetime """ - return self.get_quote_list(stock,'last_updated_at') + #TODO: recast to datetime object? + return self.get_quote_list(stock,'last_updated_at') - def get_account(self): - """fetch account information - Returns: - (:obj:`dict`): `accounts` endpoint payload + def get_account(self): + """ + Fetch account information + Returns: + (:obj:`dict`): `accounts` endpoint payload """ + res = self.session.get(self.endpoints['accounts']) res.raise_for_status() #auth required res = res.json() return res['results'][0] + def get_url(self, url): - """flat wrapper for fetching URL directly""" + """ + Flat wrapper for fetching URL directly + """ return self.session.get(url).json() - ############################## - #GET FUNDAMENTALS - ############################## - def get_fundamentals(self, stock=''): - """find stock fundamentals data + ########################################################################### + # GET FUNDAMENTALS + ########################################################################### - Args: - (str): stock ticker + def get_fundamentals(self, stock=''): + """ + Find stock fundamentals data - Returns: - (:obj:`dict`): contents of `fundamentals` endpoint + Args: + (str): stock ticker + Returns: + (:obj:`dict`): contents of `fundamentals` endpoint """ + #Prompt for stock if not entered if not stock: #pragma: no cover stock = input("Symbol: ") url = str(self.endpoints['fundamentals']) + str(stock.upper()) + "/" + #Check for validity of symbol try: req = requests.get(url) @@ -551,131 +614,185 @@ def get_fundamentals(self, stock=''): def fundamentals(self, stock=''): - """wrapper for get_fundamentlals function""" + """ + Wrapper for get_fundamentlals function + """ + return self.get_fundamentals(stock) - ############################## - # PORTFOLIOS DATA - ############################## + + ########################################################################### + # PORTFOLIOS DATA + ########################################################################### def portfolios(self): - """Returns the user's portfolio data.""" + """ + Returns the user's portfolio data + """ + req = self.session.get(self.endpoints['portfolios']) req.raise_for_status() + return req.json()['results'][0] + def adjusted_equity_previous_close(self): - """wrapper for portfolios + """ + Wrapper for portfolios - get `adjusted_equity_previous_close` value + Returns: + (float): `adjusted_equity_previous_close` value """ return float(self.portfolios()['adjusted_equity_previous_close']) - def equity(self): - """wrapper for portfolios - get `equity` value + def equity(self): + """ + Wrapper for portfolios + Returns: + (float): `equity` value """ + return float(self.portfolios()['equity']) - def equity_previous_close(self): - """wrapper for portfolios - get `equity_previous_close` value + def equity_previous_close(self): + """ + Wrapper for portfolios + Returns: + (float): `equity_previous_close` value """ + return float(self.portfolios()['equity_previous_close']) - def excess_margin(self): - """wrapper for portfolios - get `excess_margin` value + def excess_margin(self): + """ + Wrapper for portfolios + Returns: + (float): `excess_margin` value """ + return float(self.portfolios()['excess_margin']) - def extended_hours_equity(self): - """wrapper for portfolios - get `extended_hours_equity` value + def extended_hours_equity(self): + """ + Wrapper for portfolios + Returns: + (float): `extended_hours_equity` value """ + try: return float(self.portfolios()['extended_hours_equity']) + except TypeError: return None - def extended_hours_market_value(self): - """wrapper for portfolios - get `extended_hours_market_value` value + def extended_hours_market_value(self): + """ + Wrapper for portfolios + Returns: + (float): `extended_hours_market_value` value """ + try: return float(self.portfolios()['extended_hours_market_value']) + except TypeError: return None - def last_core_equity(self): - """wrapper for portfolios - get `last_core_equity` value + def last_core_equity(self): + """ + Wrapper for portfolios + Returns: + (float): `last_core_equity` value """ + return float(self.portfolios()['last_core_equity']) - def last_core_market_value(self): - """wrapper for portfolios - get `last_core_market_value` value + def last_core_market_value(self): + """ + Wrapper for portfolios + Returns: + (float): `last_core_market_value` value """ + return float(self.portfolios()['last_core_market_value']) - def market_value(self): - """wrapper for portfolios - get `market_value` value + def market_value(self): + """ + Wrapper for portfolios + Returns: + (float): `market_value` value """ + return float(self.portfolios()['market_value']) - def order_history(self): - """wrapper for portfolios - get orders from account + def order_history(self): + """ + Wrapper for portfolios + Returns: + (:obj:`dict`): JSON dict from getting orders """ + return self.session.get(self.endpoints['orders']).json() - def dividends(self): - """wrapper for portfolios - get dividends from account + def dividends(self): + """ + Wrapper for portfolios + Returns: + (:obj: `dict`): JSON dict from getting dividends """ return self.session.get(self.endpoints['dividends']).json() - ############################## - # POSITIONS DATA - ############################## + + ########################################################################### + # POSITIONS DATA + ########################################################################### def positions(self): - """Returns the user's positions data.""" + """ + Returns the user's positions data + + Returns: + (:object: `dict`): JSON dict from getting positions + """ + return self.session.get(self.endpoints['positions']).json() + def securities_owned(self): """ - Returns a list of symbols of securities of which there are more - than zero shares in user's portfolio. + Returns a list of symbols of securities of which there are more + than zero shares in user's portfolio. + + Returns: + (:object: `dict`): Non-zero positions """ return self.session.get(self.endpoints['positions']+'?nonzero=true').json() - ############################## - #PLACE ORDER - ############################## + + ########################################################################### + # PLACE ORDER + ########################################################################### def place_order( self, @@ -687,32 +804,35 @@ def place_order( order='market', time_in_force = 'gfd' ): - """place an order with Robinhood - - Notes: - OMFG TEST THIS PLEASE! + """ + Place an order with Robinhood - Just realized this won't work since if type is LIMIT you need to use "price" and if - a STOP you need to use "stop_price". Oops. - Reference: https://github.com/sanko/Robinhood/blob/master/Order.md#place-an-order + Notes: + OMFG TEST THIS PLEASE! - Args: - instrument (dict): the RH URL and symbol in dict for the instrument to be traded - quantity (int): quantity of stocks in order - bid_price (float): price for order - transaction (:enum:`Transaction`): BUY or SELL enum - trigger (:enum:`Trigger`): IMMEDIATE or STOP enum - order (:enum:`Order`): MARKET or LIMIT - time_in_force (:enum:`TIME_IN_FORCE`): GFD or GTC (day or until cancelled) + Just realized this won't work since if type is LIMIT you need to use "price" and if + a STOP you need to use "stop_price". Oops. + Reference: https://github.com/sanko/Robinhood/blob/master/Order.md#place-an-order - Returns: - (:obj:`requests.request`): result from `orders` put command + Args: + instrument (dict): the RH URL and symbol in dict for the instrument to be traded + quantity (int): quantity of stocks in order + bid_price (float): price for order + transaction (:enum:`Transaction`): BUY or SELL enum + trigger (:enum:`Trigger`): IMMEDIATE or STOP enum + order (:enum:`Order`): MARKET or LIMIT + time_in_force (:enum:`TIME_IN_FORCE`): GFD or GTC (day or until cancelled) + Returns: + (:obj:`requests.request`): result from `orders` put command """ + if isinstance(transaction, str): transaction = Transaction(transaction) + if not bid_price: bid_price = self.quote_data(instrument['symbol'])['bid_price'] + payload = { 'account': self.get_account()['url'], 'instrument': unquote(instrument['url']), @@ -724,6 +844,7 @@ def place_order( 'trigger': trigger, 'type': order.lower() } + #data = 'account=%s&instrument=%s&price=%f&quantity=%d&side=%s&symbol=%s#&time_in_force=gfd&trigger=immediate&type=market' % ( # self.get_account()['url'], # urllib.parse.unquote(instrument['url']), @@ -732,49 +853,45 @@ def place_order( # transaction, # instrument['symbol'] #) - res = self.session.post( - self.endpoints['orders'], - data=payload - ) + + res = self.session.post(self.endpoints['orders'], data=payload) res.raise_for_status() + return res - def place_buy_order( - self, - instrument, - quantity, - bid_price=0.0 - ): - """wrapper for placing buy orders - Args: - instrument (dict): the RH URL and symbol in dict for the instrument to be traded - quantity (int): quantity of stocks in order - bid_price (float): price for order + def place_buy_order(self, instrument, quantity, bid_price=0.0): + """ + Wrapper for placing buy orders - Returns: - (:obj:`requests.request`): result from `orders` put command + Args: + instrument (dict): the RH URL and symbol in dict for the instrument to be traded + quantity (int): quantity of stocks in order + bid_price (float): price for order + + Returns: + (:obj:`requests.request`): result from `orders` put command """ + transaction = Transaction.BUY + return self.place_order(instrument, quantity, bid_price, transaction) - def place_sell_order( - self, - instrument, - quantity, - bid_price=0.0 - ): - """wrapper for placing sell orders - Args: - instrument (dict): the RH URL and symbol in dict for the instrument to be traded - quantity (int): quantity of stocks in order - bid_price (float): price for order + def place_sell_order(self, instrument, quantity, bid_price=0.0): + """ + Wrapper for placing sell orders - Returns: - (:obj:`requests.request`): result from `orders` put command + Args: + instrument (dict): the RH URL and symbol in dict for the instrument to be traded + quantity (int): quantity of stocks in order + bid_price (float): price for order + Returns: + (:obj:`requests.request`): result from `orders` put command """ + transaction = Transaction.SELL + return self.place_order(instrument, quantity, bid_price, transaction) From 183b478666124a13802f286a8981934ae7ead2ec Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Thu, 26 Oct 2017 22:09:24 +0200 Subject: [PATCH 2/8] Uniform + consistent + spacious: documentation, follows most PEP 8 stands --- Robinhood/exceptions.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/Robinhood/exceptions.py b/Robinhood/exceptions.py index 9f8bbb2a..7d2cfa94 100644 --- a/Robinhood/exceptions.py +++ b/Robinhood/exceptions.py @@ -1,10 +1,26 @@ -"""exceptions: custom exceptions for library""" +""" + Exceptions: custom exceptions for library +""" + + class RobinhoodException(Exception): - """wrapper for custom Robinhood library exceptions""" + """ + Wrapper for custom Robinhood library exceptions + """ + pass + + class LoginFailed(RobinhoodException): - """Unable to login to robinhood""" + """ + Unable to login to Robinhood + """ pass + + class TwoFactorRequired(LoginFailed): - """Unable to login thanks to 2FA failure""" + """ + Unable to login because of 2FA failure + """ + pass From 09b515fd7d07644eb35b9c11a8aa848dc001b45e Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Thu, 26 Oct 2017 22:10:31 +0200 Subject: [PATCH 3/8] Added invalid ticker symbol Exception --- Robinhood/exceptions.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Robinhood/exceptions.py b/Robinhood/exceptions.py index 7d2cfa94..149f3051 100644 --- a/Robinhood/exceptions.py +++ b/Robinhood/exceptions.py @@ -24,3 +24,11 @@ class TwoFactorRequired(LoginFailed): """ pass + + +class InvalidTickerSymbol(RobinhoodException): + """ + When an invalid ticker (stock symbol) is given + """ + + pass From 7bcefc92064ff24bd3339a3ddca1e4cdd6e8c347 Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Thu, 26 Oct 2017 22:24:36 +0200 Subject: [PATCH 4/8] Additional method to return last_updated_at as a datetime object --- Robinhood/Robinhood.py | 58 +++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/Robinhood/Robinhood.py b/Robinhood/Robinhood.py index 74169f92..02b129d1 100644 --- a/Robinhood/Robinhood.py +++ b/Robinhood/Robinhood.py @@ -20,6 +20,7 @@ class Bounds(Enum): """ Enum for bounds in `historicals` endpoint """ + REGULAR = 'regular' EXTENDED = 'extended' @@ -28,6 +29,7 @@ class Transaction(Enum): """ Enum for buy/sell orders """ + BUY = 'buy' SELL = 'sell' @@ -66,13 +68,9 @@ class Robinhood: } session = None - username = None - password = None - headers = None - auth_token = None logger = logging.getLogger('Robinhood') @@ -95,14 +93,21 @@ def __init__(self): "Connection": "keep-alive", "User-Agent": "Robinhood/823 (iPhone; iOS 7.1.2; Scale/2.00)" } + self.session.headers = self.headers + def login_prompt(self): #pragma: no cover - """Prompts user for username and password and calls login().""" + """ + Prompts user for username and password and calls login() + """ + username = input("Username: ") password = getpass.getpass() + return self.login(username=username, password=password) + def login(self, username, password, mfa_code=None): """ Save and test login info for Robinhood accounts @@ -166,7 +171,7 @@ def logout(self): ########################################################################### - # GET DATA + # GET DATA ########################################################################### def investment_profile(self): """ @@ -176,6 +181,7 @@ def investment_profile(self): res = self.session.get(self.endpoints['investment_profile']) res.raise_for_status() #will throw without auth data = res.json() + return data @@ -215,6 +221,7 @@ def quote_data(self, stock=''): """ url = None + if stock.find(',') == -1: url = str(self.endpoints['quotes']) + str(stock) + "/" else: @@ -556,8 +563,29 @@ def last_updated_at(self, stock=''): (str): last update datetime """ - #TODO: recast to datetime object? - return self.get_quote_list(stock,'last_updated_at') + return self.get_quote_list(stock, 'last_updated_at') + + + def last_updated_at_datetime(self, stock=''): + """ + Get last updated datetime + + Note: + queries `quote` endpoint, dict wrapper + + Args: + stock (str): stock ticker + + Returns: + (datetime): last update datetime + + """ + + #Will be in format: 'YYYY-MM-ddTHH:mm:ss:000Z' + datetime_string = self.last_updated_at(stock) + result = dateutil.parser.parse(datetime_string) + + return result def get_account(self): @@ -571,6 +599,7 @@ def get_account(self): res = self.session.get(self.endpoints['accounts']) res.raise_for_status() #auth required res = res.json() + return res['results'][0] @@ -578,11 +607,12 @@ def get_url(self, url): """ Flat wrapper for fetching URL directly """ + return self.session.get(url).json() ########################################################################### - # GET FUNDAMENTALS + # GET FUNDAMENTALS ########################################################################### def get_fundamentals(self, stock=''): @@ -622,7 +652,7 @@ def fundamentals(self, stock=''): ########################################################################### - # PORTFOLIOS DATA + # PORTFOLIOS DATA ########################################################################### def portfolios(self): @@ -644,6 +674,7 @@ def adjusted_equity_previous_close(self): (float): `adjusted_equity_previous_close` value """ + return float(self.portfolios()['adjusted_equity_previous_close']) @@ -761,11 +792,12 @@ def dividends(self): Returns: (:obj: `dict`): JSON dict from getting dividends """ + return self.session.get(self.endpoints['dividends']).json() ########################################################################### - # POSITIONS DATA + # POSITIONS DATA ########################################################################### def positions(self): @@ -787,11 +819,12 @@ def securities_owned(self): Returns: (:object: `dict`): Non-zero positions """ + return self.session.get(self.endpoints['positions']+'?nonzero=true').json() ########################################################################### - # PLACE ORDER + # PLACE ORDER ########################################################################### def place_order( @@ -895,3 +928,4 @@ def place_sell_order(self, instrument, quantity, bid_price=0.0): transaction = Transaction.SELL return self.place_order(instrument, quantity, bid_price, transaction) + From b35923a265e6df95fdd0add5464cba189bd5f90e Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Thu, 26 Oct 2017 22:26:48 +0200 Subject: [PATCH 5/8] Added custom exception + removed finished TODOs --- Robinhood/Robinhood.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Robinhood/Robinhood.py b/Robinhood/Robinhood.py index 02b129d1..3ff8aa4e 100644 --- a/Robinhood/Robinhood.py +++ b/Robinhood/Robinhood.py @@ -232,8 +232,10 @@ def quote_data(self, stock=''): req = requests.get(url) req.raise_for_status() data = req.json() + except requests.exceptions.HTTPError: - raise NameError('Invalid Symbol: ' + stock) #TODO: custom exception + raise RH_exception.InvalidTickerSymbol() + return data @@ -252,12 +254,15 @@ def quotes_data(self, stocks): """ url = str(self.endpoints['quotes']) + "?symbols=" + ",".join(stocks) + try: req = requests.get(url) req.raise_for_status() data = req.json() + except requests.exceptions.HTTPError: - raise NameError('Invalid Symbols: ' + ",".join(stocks)) #TODO: custom exception + raise RH_exception.InvalidTickerSymbol() + return data["results"] @@ -637,8 +642,10 @@ def get_fundamentals(self, stock=''): req = requests.get(url) req.raise_for_status() data = req.json() + except requests.exceptions.HTTPError: - raise NameError('Invalid Symbol: ' + stock) #TODO wrap custom exception + raise RH_exception.InvalidTickerSymbol() + return data From 2bc8f0c21a462a6efc5733dcd1f96b69b767790f Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Fri, 27 Oct 2017 01:21:59 +0200 Subject: [PATCH 6/8] Added 's comments + recommendations --- Robinhood/Robinhood.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Robinhood/Robinhood.py b/Robinhood/Robinhood.py index 3ff8aa4e..e9bc49c5 100644 --- a/Robinhood/Robinhood.py +++ b/Robinhood/Robinhood.py @@ -2,18 +2,23 @@ Robinhood.py: a collection of utilities for working with Robinhood's Private API """ +#Standard libraries +import logging +import warnings + from enum import Enum + +#External dependencies from six.moves.urllib.parse import unquote from six.moves.urllib.request import getproxies from six.moves import input -from . import exceptions as RH_exception import getpass import requests import six -import logging -import warnings +#Application-specific imports +from . import exceptions as RH_exception class Bounds(Enum): @@ -151,7 +156,7 @@ def login(self, username, password, mfa_code=None): def logout(self): """ - Sogout from Robinhood + Logout from Robinhood Returns: (:obj:`requests.request`) result from logout endpoint @@ -196,9 +201,7 @@ def instruments(self, stock): (:obj:`dict`): JSON contents from `instruments` endpoint """ - res = self.session.get(self.endpoints['instruments'], - params={'query': stock.upper()} - ) + res = self.session.get(self.endpoints['instruments'], params={'query': stock.upper()}) res.raise_for_status() res = res.json() @@ -232,7 +235,6 @@ def quote_data(self, stock=''): req = requests.get(url) req.raise_for_status() data = req.json() - except requests.exceptions.HTTPError: raise RH_exception.InvalidTickerSymbol() @@ -259,7 +261,6 @@ def quotes_data(self, stocks): req = requests.get(url) req.raise_for_status() data = req.json() - except requests.exceptions.HTTPError: raise RH_exception.InvalidTickerSymbol() @@ -577,6 +578,7 @@ def last_updated_at_datetime(self, stock=''): Note: queries `quote` endpoint, dict wrapper + `self.last_updated_at` returns time as `str` in format: 'YYYY-MM-ddTHH:mm:ss:000Z' Args: stock (str): stock ticker @@ -642,7 +644,6 @@ def get_fundamentals(self, stock=''): req = requests.get(url) req.raise_for_status() data = req.json() - except requests.exceptions.HTTPError: raise RH_exception.InvalidTickerSymbol() @@ -728,7 +729,6 @@ def extended_hours_equity(self): try: return float(self.portfolios()['extended_hours_equity']) - except TypeError: return None @@ -743,7 +743,6 @@ def extended_hours_market_value(self): try: return float(self.portfolios()['extended_hours_market_value']) - except TypeError: return None @@ -820,8 +819,7 @@ def positions(self): def securities_owned(self): """ - Returns a list of symbols of securities of which there are more - than zero shares in user's portfolio. + Returns list of securities' symbols that the user has shares in Returns: (:object: `dict`): Non-zero positions From 641a30b4e89a6f4dbd0c74858199d0b81c01a288 Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Fri, 27 Oct 2017 02:21:21 +0200 Subject: [PATCH 7/8] Fixed to adhere to Sphinx standards --- Robinhood/Robinhood.py | 189 ++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 116 deletions(-) diff --git a/Robinhood/Robinhood.py b/Robinhood/Robinhood.py index e9bc49c5..a7975874 100644 --- a/Robinhood/Robinhood.py +++ b/Robinhood/Robinhood.py @@ -1,6 +1,4 @@ -""" - Robinhood.py: a collection of utilities for working with Robinhood's Private API -""" +""" Robinhood.py: a collection of utilities for working with Robinhood's Private API """ #Standard libraries import logging @@ -22,18 +20,14 @@ class Bounds(Enum): - """ - Enum for bounds in `historicals` endpoint - """ + """ Enum for bounds in `historicals` endpoint """ REGULAR = 'regular' EXTENDED = 'extended' class Transaction(Enum): - """ - Enum for buy/sell orders - """ + """ Enum for buy/sell orders """ BUY = 'buy' SELL = 'sell' @@ -103,9 +97,7 @@ def __init__(self): def login_prompt(self): #pragma: no cover - """ - Prompts user for username and password and calls login() - """ + """ Prompts user for username and password and calls login() """ username = input("Username: ") password = getpass.getpass() @@ -113,9 +105,11 @@ def login_prompt(self): #pragma: no cover return self.login(username=username, password=password) - def login(self, username, password, mfa_code=None): - """ - Save and test login info for Robinhood accounts + def login(self, + username, + password, + mfa_code=None): + """ Save and test login info for Robinhood accounts Args: username (str): username @@ -155,8 +149,7 @@ def login(self, username, password, mfa_code=None): def logout(self): - """ - Logout from Robinhood + """ Logout from Robinhood Returns: (:obj:`requests.request`) result from logout endpoint @@ -178,10 +171,9 @@ def logout(self): ########################################################################### # GET DATA ########################################################################### + def investment_profile(self): - """ - Fetch investment_profile - """ + """ Fetch investment_profile """ res = self.session.get(self.endpoints['investment_profile']) res.raise_for_status() #will throw without auth @@ -191,8 +183,7 @@ def investment_profile(self): def instruments(self, stock): - """ - Fetch instruments endpoint + """ Fetch instruments endpoint Args: stock (str): stock ticker @@ -213,8 +204,7 @@ def instruments(self, stock): def quote_data(self, stock=''): - """ - Fetch stock quote + """ Fetch stock quote Args: stock (str): stock ticker, prompt if blank @@ -244,8 +234,7 @@ def quote_data(self, stock=''): # We will keep for compatibility until next major release def quotes_data(self, stocks): - """ - Fetch quote for multiple stocks, in one single Robinhood API call + """ Fetch quote for multiple stocks, in one single Robinhood API call Args: stocks (list): stock tickers @@ -268,9 +257,10 @@ def quotes_data(self, stocks): return data["results"] - def get_quote_list(self, stock='', key=''): - """ - Returns multiple stock info and keys from quote_data (prompt if blank) + def get_quote_list(self, + stock='', + key=''): + """ Returns multiple stock info and keys from quote_data (prompt if blank) Args: stock (str): stock ticker (or tickers separated by a comma) @@ -289,8 +279,10 @@ def append_stock(stock): myStr = '' for item in keys: myStr += stock[item] + "," + return (myStr.split(',')) + #Prompt for stock if not entered if not stock: #pragma: no cover stock = input("Symbol: ") @@ -312,17 +304,14 @@ def append_stock(stock): def get_quote(self, stock=''): - """ - Wrapper for quote_data - """ + """ Wrapper for quote_data """ data = self.quote_data(stock) return data["symbol"] def get_historical_quotes(self, stock, interval, span, bounds=Bounds.REGULAR): - """ - Fetch historical data for stock + """ Fetch historical data for stock Note: valid interval/span configs interval = 5minute | 10minute + span = day, week @@ -355,8 +344,7 @@ def get_historical_quotes(self, stock, interval, span, bounds=Bounds.REGULAR): def get_news(self, stock): - """ - Fetch news endpoint + """ Fetch news endpoint Args: stock (str): stock ticker @@ -368,8 +356,7 @@ def get_news(self, stock): def print_quote(self, stock=''): #pragma: no cover - """ - Print quote information + """ Print quote information Args: stock (str): ticker to fetch @@ -385,8 +372,7 @@ def print_quote(self, stock=''): #pragma: no cover def print_quotes(self, stocks): #pragma: no cover - """ - Print a collection of stocks + """ Print a collection of stocks Args: stocks (:obj:`list`): list of stocks to pirnt @@ -403,8 +389,7 @@ def print_quotes(self, stocks): #pragma: no cover def ask_price(self, stock=''): - """ - Get asking price for a stock + """ Get asking price for a stock Note: queries `quote` endpoint, dict wrapper @@ -420,8 +405,7 @@ def ask_price(self, stock=''): def ask_size(self, stock=''): - """ - Get ask size for a stock + """ Get ask size for a stock Note: queries `quote` endpoint, dict wrapper @@ -437,8 +421,7 @@ def ask_size(self, stock=''): def bid_price(self, stock=''): - """ - Get bid price for a stock + """ Get bid price for a stock Note: queries `quote` endpoint, dict wrapper @@ -454,8 +437,7 @@ def bid_price(self, stock=''): def bid_size(self, stock=''): - """ - Get bid size for a stock + """ Get bid size for a stock Note: queries `quote` endpoint, dict wrapper @@ -471,8 +453,7 @@ def bid_size(self, stock=''): def last_trade_price(self, stock=''): - """ - Get last trade price for a stock + """ Get last trade price for a stock Note: queries `quote` endpoint, dict wrapper @@ -488,8 +469,7 @@ def last_trade_price(self, stock=''): def previous_close(self, stock=''): - """ - Get previous closing price for a stock + """ Get previous closing price for a stock Note: queries `quote` endpoint, dict wrapper @@ -505,8 +485,7 @@ def previous_close(self, stock=''): def previous_close_date(self, stock=''): - """ - Get previous closing date for a stock + """ Get previous closing date for a stock Note: queries `quote` endpoint, dict wrapper @@ -522,8 +501,7 @@ def previous_close_date(self, stock=''): def adjusted_previous_close(self, stock=''): - """ - Get adjusted previous closing price for a stock + """ Get adjusted previous closing price for a stock Note: queries `quote` endpoint, dict wrapper @@ -539,8 +517,7 @@ def adjusted_previous_close(self, stock=''): def symbol(self, stock=''): - """ - Get symbol for a stock + """ Get symbol for a stock Note: queries `quote` endpoint, dict wrapper @@ -556,8 +533,7 @@ def symbol(self, stock=''): def last_updated_at(self, stock=''): - """ - Get last update datetime + """ Get last update datetime Note: queries `quote` endpoint, dict wrapper @@ -573,8 +549,7 @@ def last_updated_at(self, stock=''): def last_updated_at_datetime(self, stock=''): - """ - Get last updated datetime + """ Get last updated datetime Note: queries `quote` endpoint, dict wrapper @@ -596,8 +571,7 @@ def last_updated_at_datetime(self, stock=''): def get_account(self): - """ - Fetch account information + """ Fetch account information Returns: (:obj:`dict`): `accounts` endpoint payload @@ -623,8 +597,7 @@ def get_url(self, url): ########################################################################### def get_fundamentals(self, stock=''): - """ - Find stock fundamentals data + """ Find stock fundamentals data Args: (str): stock ticker @@ -652,9 +625,7 @@ def get_fundamentals(self, stock=''): def fundamentals(self, stock=''): - """ - Wrapper for get_fundamentlals function - """ + """ Wrapper for get_fundamentlals function """ return self.get_fundamentals(stock) @@ -664,9 +635,7 @@ def fundamentals(self, stock=''): ########################################################################### def portfolios(self): - """ - Returns the user's portfolio data - """ + """ Returns the user's portfolio data """ req = self.session.get(self.endpoints['portfolios']) req.raise_for_status() @@ -675,8 +644,7 @@ def portfolios(self): def adjusted_equity_previous_close(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `adjusted_equity_previous_close` value @@ -687,8 +655,7 @@ def adjusted_equity_previous_close(self): def equity(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `equity` value @@ -698,8 +665,7 @@ def equity(self): def equity_previous_close(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `equity_previous_close` value @@ -709,8 +675,7 @@ def equity_previous_close(self): def excess_margin(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `excess_margin` value @@ -720,8 +685,7 @@ def excess_margin(self): def extended_hours_equity(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `extended_hours_equity` value @@ -734,8 +698,7 @@ def extended_hours_equity(self): def extended_hours_market_value(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `extended_hours_market_value` value @@ -748,8 +711,7 @@ def extended_hours_market_value(self): def last_core_equity(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `last_core_equity` value @@ -759,8 +721,7 @@ def last_core_equity(self): def last_core_market_value(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `last_core_market_value` value @@ -770,8 +731,7 @@ def last_core_market_value(self): def market_value(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (float): `market_value` value @@ -781,8 +741,7 @@ def market_value(self): def order_history(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (:obj:`dict`): JSON dict from getting orders @@ -792,8 +751,7 @@ def order_history(self): def dividends(self): - """ - Wrapper for portfolios + """ Wrapper for portfolios Returns: (:obj: `dict`): JSON dict from getting dividends @@ -807,8 +765,7 @@ def dividends(self): ########################################################################### def positions(self): - """ - Returns the user's positions data + """ Returns the user's positions data Returns: (:object: `dict`): JSON dict from getting positions @@ -818,8 +775,7 @@ def positions(self): def securities_owned(self): - """ - Returns list of securities' symbols that the user has shares in + """ Returns list of securities' symbols that the user has shares in Returns: (:object: `dict`): Non-zero positions @@ -832,18 +788,15 @@ def securities_owned(self): # PLACE ORDER ########################################################################### - def place_order( - self, - instrument, - quantity=1, - bid_price=0.0, - transaction=None, - trigger='immediate', - order='market', - time_in_force = 'gfd' - ): - """ - Place an order with Robinhood + def place_order(self, + instrument, + quantity=1, + bid_price=0.0, + transaction=None, + trigger='immediate', + order='market', + time_in_force = 'gfd'): + """ Place an order with Robinhood Notes: OMFG TEST THIS PLEASE! @@ -898,9 +851,11 @@ def place_order( return res - def place_buy_order(self, instrument, quantity, bid_price=0.0): - """ - Wrapper for placing buy orders + def place_buy_order(self, + instrument, + quantity, + bid_price=0.0): + """ Wrapper for placing buy orders Args: instrument (dict): the RH URL and symbol in dict for the instrument to be traded @@ -917,9 +872,11 @@ def place_buy_order(self, instrument, quantity, bid_price=0.0): return self.place_order(instrument, quantity, bid_price, transaction) - def place_sell_order(self, instrument, quantity, bid_price=0.0): - """ - Wrapper for placing sell orders + def place_sell_order(self, + instrument, + quantity, + bid_price=0.0): + """ Wrapper for placing sell orders Args: instrument (dict): the RH URL and symbol in dict for the instrument to be traded From 4e86f22c5df2fda67e4b258d12ec445683b7155b Mon Sep 17 00:00:00 2001 From: Ryan D'souza Date: Fri, 27 Oct 2017 02:46:59 +0200 Subject: [PATCH 8/8] Fixed to adhere to Sphinx standards --- Robinhood/Robinhood.py | 98 +++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/Robinhood/Robinhood.py b/Robinhood/Robinhood.py index a7975874..59c217bd 100644 --- a/Robinhood/Robinhood.py +++ b/Robinhood/Robinhood.py @@ -1,4 +1,4 @@ -""" Robinhood.py: a collection of utilities for working with Robinhood's Private API """ +"""Robinhood.py: a collection of utilities for working with Robinhood's Private API """ #Standard libraries import logging @@ -20,23 +20,21 @@ class Bounds(Enum): - """ Enum for bounds in `historicals` endpoint """ + """Enum for bounds in `historicals` endpoint """ REGULAR = 'regular' EXTENDED = 'extended' class Transaction(Enum): - """ Enum for buy/sell orders """ + """Enum for buy/sell orders """ BUY = 'buy' SELL = 'sell' class Robinhood: - """ - Wrapper class for fetching/parsing Robinhood endpoints - """ + """Wrapper class for fetching/parsing Robinhood endpoints """ endpoints = { "login": "https://api.robinhood.com/api-token-auth/", @@ -97,7 +95,7 @@ def __init__(self): def login_prompt(self): #pragma: no cover - """ Prompts user for username and password and calls login() """ + """Prompts user for username and password and calls login() """ username = input("Username: ") password = getpass.getpass() @@ -109,7 +107,7 @@ def login(self, username, password, mfa_code=None): - """ Save and test login info for Robinhood accounts + """Save and test login info for Robinhood accounts Args: username (str): username @@ -149,7 +147,7 @@ def login(self, def logout(self): - """ Logout from Robinhood + """Logout from Robinhood Returns: (:obj:`requests.request`) result from logout endpoint @@ -173,7 +171,7 @@ def logout(self): ########################################################################### def investment_profile(self): - """ Fetch investment_profile """ + """Fetch investment_profile """ res = self.session.get(self.endpoints['investment_profile']) res.raise_for_status() #will throw without auth @@ -183,7 +181,7 @@ def investment_profile(self): def instruments(self, stock): - """ Fetch instruments endpoint + """Fetch instruments endpoint Args: stock (str): stock ticker @@ -204,7 +202,7 @@ def instruments(self, stock): def quote_data(self, stock=''): - """ Fetch stock quote + """Fetch stock quote Args: stock (str): stock ticker, prompt if blank @@ -234,7 +232,7 @@ def quote_data(self, stock=''): # We will keep for compatibility until next major release def quotes_data(self, stocks): - """ Fetch quote for multiple stocks, in one single Robinhood API call + """Fetch quote for multiple stocks, in one single Robinhood API call Args: stocks (list): stock tickers @@ -260,7 +258,7 @@ def quotes_data(self, stocks): def get_quote_list(self, stock='', key=''): - """ Returns multiple stock info and keys from quote_data (prompt if blank) + """Returns multiple stock info and keys from quote_data (prompt if blank) Args: stock (str): stock ticker (or tickers separated by a comma) @@ -304,14 +302,14 @@ def append_stock(stock): def get_quote(self, stock=''): - """ Wrapper for quote_data """ + """Wrapper for quote_data """ data = self.quote_data(stock) return data["symbol"] def get_historical_quotes(self, stock, interval, span, bounds=Bounds.REGULAR): - """ Fetch historical data for stock + """Fetch historical data for stock Note: valid interval/span configs interval = 5minute | 10minute + span = day, week @@ -344,7 +342,7 @@ def get_historical_quotes(self, stock, interval, span, bounds=Bounds.REGULAR): def get_news(self, stock): - """ Fetch news endpoint + """Fetch news endpoint Args: stock (str): stock ticker @@ -356,7 +354,7 @@ def get_news(self, stock): def print_quote(self, stock=''): #pragma: no cover - """ Print quote information + """Print quote information Args: stock (str): ticker to fetch @@ -372,7 +370,7 @@ def print_quote(self, stock=''): #pragma: no cover def print_quotes(self, stocks): #pragma: no cover - """ Print a collection of stocks + """Print a collection of stocks Args: stocks (:obj:`list`): list of stocks to pirnt @@ -389,7 +387,7 @@ def print_quotes(self, stocks): #pragma: no cover def ask_price(self, stock=''): - """ Get asking price for a stock + """Get asking price for a stock Note: queries `quote` endpoint, dict wrapper @@ -405,7 +403,7 @@ def ask_price(self, stock=''): def ask_size(self, stock=''): - """ Get ask size for a stock + """Get ask size for a stock Note: queries `quote` endpoint, dict wrapper @@ -421,7 +419,7 @@ def ask_size(self, stock=''): def bid_price(self, stock=''): - """ Get bid price for a stock + """Get bid price for a stock Note: queries `quote` endpoint, dict wrapper @@ -437,7 +435,7 @@ def bid_price(self, stock=''): def bid_size(self, stock=''): - """ Get bid size for a stock + """Get bid size for a stock Note: queries `quote` endpoint, dict wrapper @@ -453,7 +451,7 @@ def bid_size(self, stock=''): def last_trade_price(self, stock=''): - """ Get last trade price for a stock + """Get last trade price for a stock Note: queries `quote` endpoint, dict wrapper @@ -469,7 +467,7 @@ def last_trade_price(self, stock=''): def previous_close(self, stock=''): - """ Get previous closing price for a stock + """Get previous closing price for a stock Note: queries `quote` endpoint, dict wrapper @@ -485,7 +483,7 @@ def previous_close(self, stock=''): def previous_close_date(self, stock=''): - """ Get previous closing date for a stock + """Get previous closing date for a stock Note: queries `quote` endpoint, dict wrapper @@ -501,7 +499,7 @@ def previous_close_date(self, stock=''): def adjusted_previous_close(self, stock=''): - """ Get adjusted previous closing price for a stock + """Get adjusted previous closing price for a stock Note: queries `quote` endpoint, dict wrapper @@ -517,7 +515,7 @@ def adjusted_previous_close(self, stock=''): def symbol(self, stock=''): - """ Get symbol for a stock + """Get symbol for a stock Note: queries `quote` endpoint, dict wrapper @@ -533,7 +531,7 @@ def symbol(self, stock=''): def last_updated_at(self, stock=''): - """ Get last update datetime + """Get last update datetime Note: queries `quote` endpoint, dict wrapper @@ -549,7 +547,7 @@ def last_updated_at(self, stock=''): def last_updated_at_datetime(self, stock=''): - """ Get last updated datetime + """Get last updated datetime Note: queries `quote` endpoint, dict wrapper @@ -571,7 +569,7 @@ def last_updated_at_datetime(self, stock=''): def get_account(self): - """ Fetch account information + """Fetch account information Returns: (:obj:`dict`): `accounts` endpoint payload @@ -597,7 +595,7 @@ def get_url(self, url): ########################################################################### def get_fundamentals(self, stock=''): - """ Find stock fundamentals data + """Find stock fundamentals data Args: (str): stock ticker @@ -625,7 +623,7 @@ def get_fundamentals(self, stock=''): def fundamentals(self, stock=''): - """ Wrapper for get_fundamentlals function """ + """Wrapper for get_fundamentlals function """ return self.get_fundamentals(stock) @@ -635,7 +633,7 @@ def fundamentals(self, stock=''): ########################################################################### def portfolios(self): - """ Returns the user's portfolio data """ + """Returns the user's portfolio data """ req = self.session.get(self.endpoints['portfolios']) req.raise_for_status() @@ -644,7 +642,7 @@ def portfolios(self): def adjusted_equity_previous_close(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `adjusted_equity_previous_close` value @@ -655,7 +653,7 @@ def adjusted_equity_previous_close(self): def equity(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `equity` value @@ -665,7 +663,7 @@ def equity(self): def equity_previous_close(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `equity_previous_close` value @@ -675,7 +673,7 @@ def equity_previous_close(self): def excess_margin(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `excess_margin` value @@ -685,7 +683,7 @@ def excess_margin(self): def extended_hours_equity(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `extended_hours_equity` value @@ -698,7 +696,7 @@ def extended_hours_equity(self): def extended_hours_market_value(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `extended_hours_market_value` value @@ -711,7 +709,7 @@ def extended_hours_market_value(self): def last_core_equity(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `last_core_equity` value @@ -721,7 +719,7 @@ def last_core_equity(self): def last_core_market_value(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `last_core_market_value` value @@ -731,7 +729,7 @@ def last_core_market_value(self): def market_value(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (float): `market_value` value @@ -741,7 +739,7 @@ def market_value(self): def order_history(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (:obj:`dict`): JSON dict from getting orders @@ -751,7 +749,7 @@ def order_history(self): def dividends(self): - """ Wrapper for portfolios + """Wrapper for portfolios Returns: (:obj: `dict`): JSON dict from getting dividends @@ -765,7 +763,7 @@ def dividends(self): ########################################################################### def positions(self): - """ Returns the user's positions data + """Returns the user's positions data Returns: (:object: `dict`): JSON dict from getting positions @@ -775,7 +773,7 @@ def positions(self): def securities_owned(self): - """ Returns list of securities' symbols that the user has shares in + """Returns list of securities' symbols that the user has shares in Returns: (:object: `dict`): Non-zero positions @@ -796,7 +794,7 @@ def place_order(self, trigger='immediate', order='market', time_in_force = 'gfd'): - """ Place an order with Robinhood + """Place an order with Robinhood Notes: OMFG TEST THIS PLEASE! @@ -855,7 +853,7 @@ def place_buy_order(self, instrument, quantity, bid_price=0.0): - """ Wrapper for placing buy orders + """Wrapper for placing buy orders Args: instrument (dict): the RH URL and symbol in dict for the instrument to be traded @@ -876,7 +874,7 @@ def place_sell_order(self, instrument, quantity, bid_price=0.0): - """ Wrapper for placing sell orders + """Wrapper for placing sell orders Args: instrument (dict): the RH URL and symbol in dict for the instrument to be traded