From cf7cb6b0356cab2dc6ffeb68f908dee3b170620a Mon Sep 17 00:00:00 2001 From: josh fernandes Date: Sat, 20 Jun 2020 22:10:48 -0700 Subject: [PATCH] improved readability of option functions combined the list and regular option functions, changed names, and improved functionality of several option functions. --- robin_stocks/__init__.py | 12 +- robin_stocks/options.py | 349 ++++++++++++++++++--------------------- setup.py | 2 +- 3 files changed, 164 insertions(+), 199 deletions(-) diff --git a/robin_stocks/__init__.py b/robin_stocks/__init__.py index e31682cf..f1fc1108 100644 --- a/robin_stocks/__init__.py +++ b/robin_stocks/__init__.py @@ -55,13 +55,11 @@ get_all_option_positions, \ get_open_option_positions, \ get_chains, \ - find_tradable_options_for_stock, \ - find_options_for_stock_by_expiration, \ - find_options_for_stock_by_strike, \ - find_options_for_stock_by_expiration_and_strike, \ - find_options_for_list_of_stocks_by_expiration_date,\ - get_list_market_data, \ - get_list_options_of_specific_profitability, \ + find_tradable_options, \ + find_options_by_expiration, \ + find_options_by_strike, \ + find_options_by_expiration_and_strike, \ + find_options_by_specific_profitability, \ get_option_market_data_by_id, \ get_option_market_data, \ get_option_instrument_data_by_id, \ diff --git a/robin_stocks/options.py b/robin_stocks/options.py index b7aa0e5c..d65145cc 100644 --- a/robin_stocks/options.py +++ b/robin_stocks/options.py @@ -1,7 +1,23 @@ """Contains functions for getting information about options.""" import robin_stocks.helper as helper import robin_stocks.urls as urls +import sys +def spinning_cursor(): + """ This is a generator function to yield a character. """ + while True: + for cursor in '|/-\\': + yield cursor + +spinner = spinning_cursor() + +def write_spinner(): + """ Function to create a spinning cursor to tell user that the code is working on getting market data. """ + marketString = 'Loading Market Data ' + sys.stdout.write(marketString) + sys.stdout.write(next(spinner)) + sys.stdout.flush() + sys.stdout.write('\b'*(len(marketString)+1)) @helper.login_required def get_aggregate_positions(info=None): @@ -89,11 +105,15 @@ def get_chains(symbol, info=None): return(helper.filter(data, info)) -def find_tradable_options_for_stock(symbol, optionType='both', info=None): +def find_tradable_options(symbol, expirationDate=None, strikePrice=None, optionType=None, info=None): """Returns a list of all available options for a stock. :param symbol: The ticker of the stock. :type symbol: str + :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. + :type expirationDate: str + :param strikePrice: Represents the strike price of the option. + :type strikePrice: str :param optionType: Can be either 'call' or 'put' or left blank to get both. :type optionType: Optional[str] :param info: Will filter the results to get a specific value. @@ -104,7 +124,6 @@ def find_tradable_options_for_stock(symbol, optionType='both', info=None): """ try: symbol = symbol.upper().strip() - optionType = optionType.lower().strip() except AttributeError as message: print(message) return [None] @@ -112,28 +131,28 @@ def find_tradable_options_for_stock(symbol, optionType='both', info=None): url = urls.option_instruments() if not helper.id_for_chain(symbol): print("Symbol {} is not valid for finding options.".format(symbol)) + return [None] - if (optionType == 'call' or optionType == 'put'): - payload = {'chain_id': helper.id_for_chain(symbol), - 'chain_symbol': symbol, - 'state': 'active', - 'tradability': 'tradable', - 'type': optionType} - else: - payload = {'chain_id': helper.id_for_chain(symbol), - 'chain_symbol': symbol, - 'state': 'active', - 'tradability': 'tradable'} + payload = {'chain_id': helper.id_for_chain(symbol), + 'chain_symbol': symbol, + 'state': 'active'} + + if expirationDate: + payload['expiration_date'] = expirationDate + if strikePrice: + payload['strike_price'] = strikePrice + if optionType: + payload['type'] = optionType data = helper.request_get(url, 'pagination', payload) return(helper.filter(data, info)) -def find_options_for_stock_by_expiration(symbol, expirationDate, optionType='both', info=None): +def find_options_by_expiration(inputSymbols, expirationDate, optionType=None, info=None): """Returns a list of all the option orders that match the seach parameters - :param symbol: The ticker of the stock. - :type symbol: str + :param inputSymbols: The ticker of either a single stock or a list of stocks. + :type inputSymbols: str :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. :type expirationDate: str :param optionType: Can be either 'call' or 'put' or leave blank to get both. @@ -145,65 +164,35 @@ def find_options_for_stock_by_expiration(symbol, expirationDate, optionType='bot """ try: - symbol = symbol.upper().strip() - optionType = optionType.lower().strip() + symbols = helper.inputs_to_set(inputSymbols) + if optionType: + optionType = optionType.lower().strip() except AttributeError as message: print(message) return [None] - allOptions = find_tradable_options_for_stock(symbol, optionType) - filteredOptions = [item for item in allOptions if item.get("expiration_date") == expirationDate - and item.get('tradability') == 'tradable'] - - for item in filteredOptions: - marketData = get_option_market_data_by_id(item['id']) - item.update(marketData) - - return(helper.filter(filteredOptions, info)) - - -def find_options_for_stock_by_strike(symbol, strike, optionType='both', info=None): - """Returns a list of all the option orders that match the seach parameters - - :param symbol: The ticker of the stock. - :type symbol: str - :param strike: Represents the price of the option. - :type strike: str - :param optionType: Can be either 'call' or 'put' or leave blank to get both. - :type optionType: Optional[str] - :param info: Will filter the results to get a specific value. - :type info: Optional[str] - :returns: Returns a list of dictionaries of key/value pairs for all options of the stock that match the search parameters. \ - If info parameter is provided, a list of strings is returned where the strings are the value of the key that matches info. - - """ - try: - symbol = symbol.upper().strip() - optionType = optionType.lower().strip() - except AttributeError as message: - print(message) - return [None] + data = [] + for symbol in symbols: + allOptions = find_tradable_options(symbol, expirationDate, None, optionType, None) + filteredOptions = [item for item in allOptions if item.get("expiration_date") == expirationDate] - allOptions = find_tradable_options_for_stock(symbol, optionType) - filteredOptions = [item for item in allOptions if float(item.get("strike_price")) == float(strike) - and item.get('tradability') == 'tradable'] + for item in filteredOptions: + marketData = get_option_market_data_by_id(item['id']) + item.update(marketData) + write_spinner() - for item in filteredOptions: - marketData = get_option_market_data_by_id(item['id']) - item.update(marketData) + data.extend(filteredOptions) - return(helper.filter(filteredOptions, info)) + return(helper.filter(data, info)) -def find_options_for_stock_by_expiration_and_strike(symbol, expirationDate, strike, optionType='both', info=None): +def find_options_by_strike(inputSymbols, strikePrice, optionType=None, info=None): """Returns a list of all the option orders that match the seach parameters - :param symbol: The ticker of the stock. - :type symbol: str - :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. - :type expirationDate: str - :param strike: Represents the price of the option. - :type strike: str + :param inputSymbols: The ticker of either a single stock or a list of stocks. + :type inputSymbols: str + :param strikePrice: Represents the strike price to filter for. + :type strikePrice: str :param optionType: Can be either 'call' or 'put' or leave blank to get both. :type optionType: Optional[str] :param info: Will filter the results to get a specific value. @@ -213,39 +202,36 @@ def find_options_for_stock_by_expiration_and_strike(symbol, expirationDate, stri """ try: - symbol = symbol.upper().strip() - option_type = optionType.lower().strip() + symbols = helper.inputs_to_set(inputSymbols) + if optionType: + optionType = optionType.lower().strip() except AttributeError as message: print(message) return [None] - url = urls.option_instruments() - payload = {'chain_id': helper.id_for_chain(symbol), - 'expiration_date': expirationDate, - 'strike_price': strike, - 'state': 'active', - 'tradability': 'tradable', - 'rhs_tradability': 'tradable'} - if option_type == 'put' or option_type == 'call': - payload['type'] = option_type + data = [] + for symbol in symbols: + filteredOptions = find_tradable_options(symbol, None, strikePrice, optionType, None) - data = helper.request_get(url, 'pagination', payload) - data = [item for item in data if item['expiration_date'] == - expirationDate and item['tradability'] == 'tradable'] - for item in data: - market_data = get_option_market_data_by_id(item['id']) - item.update(market_data) + for item in filteredOptions: + marketData = get_option_market_data_by_id(item['id']) + item.update(marketData) + write_spinner() - return helper.filter(data, info) + data.extend(filteredOptions) + + return(helper.filter(data, info)) -def find_options_for_list_of_stocks_by_expiration_date(inputSymbols, expirationDate, optionType='both', info=None): +def find_options_by_expiration_and_strike(inputSymbols, expirationDate, strikePrice, optionType=None, info=None): """Returns a list of all the option orders that match the seach parameters - :param inputSymbols: May be a single stock ticker or a list of stock tickers. - :type inputSymbols: str or list + :param inputSymbols: The ticker of either a single stock or a list of stocks. + :type inputSymbols: str :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. :type expirationDate: str + :param strikePrice: Represents the strike price to filter for. + :type strikePrice: str :param optionType: Can be either 'call' or 'put' or leave blank to get both. :type optionType: Optional[str] :param info: Will filter the results to get a specific value. @@ -254,84 +240,40 @@ def find_options_for_list_of_stocks_by_expiration_date(inputSymbols, expirationD If info parameter is provided, a list of strings is returned where the strings are the value of the key that matches info. """ - symbols = helper.inputs_to_set(inputSymbols) try: - optionType = optionType.lower().strip() + symbols = helper.inputs_to_set(inputSymbols) + if optionType: + optionType = optionType.lower().strip() except AttributeError as message: print(message) return [None] data = [] - url = urls.option_instruments() for symbol in symbols: - if (optionType == 'put' or optionType == 'call'): - payload = {'chain_id': helper.id_for_chain(symbol), - 'expiration_date': expirationDate, - 'state': 'active', - 'tradability': 'tradable', - 'rhs_tradability': 'tradable', - 'type': optionType} - else: - payload = {'chain_id': helper.id_for_chain(symbol), - 'expiration_date': expirationDate, - 'state': 'active', - 'tradability': 'tradable', - 'rhs_tradability': 'tradable'} - otherData = helper.request_get(url, 'pagination', payload) - for item in otherData: - if (item['expiration_date'] == expirationDate and item['tradability'] == 'tradable'): - data.append(item) - - for item in data: - marketData = get_option_market_data_by_id(item['id']) - item.update(marketData) + allOptions = find_tradable_options(symbol, expirationDate, strikePrice, optionType, None) + filteredOptions = [item for item in allOptions if item.get("expiration_date") == expirationDate] - return(helper.filter(data, info)) - - -def get_list_market_data(inputSymbols, expirationDate, info=None): - """Returns a list of option market data for several stock tickers. - - :param inputSymbols: May be a single stock ticker or a list of stock tickers. - :type inputSymbols: str or list - :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. - :type expirationDate: str - :param info: Will filter the results to get a specific value. - :type info: Optional[str] - :returns: Returns a list of dictionaries of key/value pairs for all stock option market data. \ - If info parameter is provided, a list of strings is returned where the strings are the value of the key that matches info. + for item in filteredOptions: + marketData = get_option_market_data_by_id(item['id']) + item.update(marketData) + write_spinner() - """ - symbols = helper.inputs_to_set(inputSymbols) - ids = [] - data = [] - url = urls.option_instruments() - for symbol in symbols: - payload = {'chain_id': helper.id_for_chain(symbol), - 'expiration_date': expirationDate, - 'state': 'active', - 'tradability': 'tradable', - 'rhs_tradability': 'tradable'} - otherData = helper.request_get(url, 'pagination', payload) - for item in otherData: - if (item['expiration_date'] == expirationDate and item['tradability'] == 'tradable'): - ids.append(item['id']) - - for id in ids: - url = urls.marketdata_options(id) - otherData = helper.request_get(url) - data.append(otherData) + data.extend(filteredOptions) - return(helper.filter(data, info)) + return helper.filter(data, info) -def get_list_options_of_specific_profitability(inputSymbols, expirationDate, typeProfit="chance_of_profit_short", profitFloor=0.0, profitCeiling=1.0, info=None): +def find_options_by_specific_profitability(inputSymbols, expirationDate=None, strikePrice=None, optionType=None, typeProfit="chance_of_profit_short", profitFloor=0.0, profitCeiling=1.0, info=None): """Returns a list of option market data for several stock tickers that match a range of profitability. :param inputSymbols: May be a single stock ticker or a list of stock tickers. :type inputSymbols: str or list - :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. + :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. Leave as None to get all available dates. :type expirationDate: str + :param strikePrice: Represents the price of the option. Leave as None to get all available strike prices. + :type strikePrice: str + :param optionType: Can be either 'call' or 'put' or leave blank to get both. + :type optionType: Optional[str] :param typeProfit: Will either be "chance_of_profit_short" or "chance_of_profit_long". :type typeProfit: str :param profitFloor: The lower percentage on scale 0 to 1. @@ -345,40 +287,30 @@ def get_list_options_of_specific_profitability(inputSymbols, expirationDate, typ """ symbols = helper.inputs_to_set(inputSymbols) - ids = [] data = [] - returnData = [] - url = urls.option_instruments() if (typeProfit != "chance_of_profit_short" and typeProfit != "chance_of_profit_long"): print("Invalid string for 'typeProfit'. Defaulting to 'chance_of_profit_short'.") typeProfit = "chance_of_profit_short" for symbol in symbols: - payload = {'chain_id': helper.id_for_chain(symbol), - 'expiration_date': expirationDate, - 'state': 'active', - 'tradability': 'tradable', - 'rhs_tradability': 'tradable'} - otherData = helper.request_get(url, 'pagination', payload) - for item in otherData: - if (item['tradability'] == 'tradable'): - ids.append(item['id']) - - for id in ids: - url = urls.marketdata_options(id) - otherData = helper.request_get(url) - data.append(otherData) - - for item in data: - try: - floatValue = float(item[typeProfit]) - if (floatValue > profitFloor and floatValue < profitCeiling): - returnData.append(item) - except: - pass - - return(helper.filter(returnData, info)) + tempData = find_tradable_options(symbol, expirationDate, strikePrice, optionType, info=None) + for option in tempData: + if expirationDate and option.get("expiration_date") != expirationDate: + continue + + market_data = get_option_market_data_by_id(option['id']) + option.update(market_data) + write_spinner() + + try: + floatValue = float(option[typeProfit]) + if (floatValue >= profitFloor and floatValue <= profitCeiling): + data.append(option) + except: + pass + + return(helper.filter(data, info)) def get_option_market_data_by_id(id, info=None): @@ -396,19 +328,51 @@ def get_option_market_data_by_id(id, info=None): url = urls.marketdata_options(id) data = helper.request_get(url) + if not data: + data= { + 'adjusted_mark_price':'', + 'ask_price':'', + 'ask_size':'', + 'bid_price':'', + 'bid_size':'', + 'break_even_price':'', + 'high_price':'', + 'instrument':'', + 'last_trade_price':'', + 'last_trade_size':'', + 'low_price':'', + 'mark_price':'', + 'open_interest':'', + 'previous_close_date':'', + 'previous_close_price':'', + 'volume':'', + 'chance_of_profit_long':'', + 'chance_of_profit_short':'', + 'delta':'', + 'gamma':'', + 'implied_volatility':'', + 'rho':'', + 'theta':'', + 'vega':'', + 'high_fill_rate_buy_price':'', + 'high_fill_rate_sell_price':'', + 'low_fill_rate_buy_price':'', + 'low_fill_rate_sell_price':'' + } + return(helper.filter(data, info)) -def get_option_market_data(symbol, expirationDate, strike, optionType, info=None): +def get_option_market_data(inputSymbols, expirationDate, strikePrice, optionType, info=None): """Returns the option market data for the stock option, including the greeks, open interest, change of profit, and adjusted mark price. - :param symbol: The ticker of the stock. - :type symbol: str + :param inputSymbols: The ticker of the stock. + :type inputSymbols: str :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. :type expirationDate: str - :param strike: Represents the price of the option. - :type strike: str + :param strikePrice: Represents the price of the option. + :type strikePrice: str :param optionType: Can be either 'call' or 'put'. :type optionType: str :param info: Will filter the results to get a specific value. @@ -418,15 +382,18 @@ def get_option_market_data(symbol, expirationDate, strike, optionType, info=None """ try: - symbol = symbol.upper().strip() - optionType = optionType.lower().strip() + symbols = helper.inputs_to_set(inputSymbols) + if optionType: + optionType = optionType.lower().strip() except AttributeError as message: print(message) return [None] - optionID = helper.id_for_option(symbol, expirationDate, strike, optionType) - url = urls.marketdata_options(optionID) - data = helper.request_get(url) + data = [] + for symbol in symbols: + optionID = helper.id_for_option(symbol, expirationDate, strikePrice, optionType) + marketData = get_option_market_data_by_id(optionID) + data.append(marketData) return(helper.filter(data, info)) @@ -447,15 +414,15 @@ def get_option_instrument_data_by_id(id, info=None): return(helper.filter(data, info)) -def get_option_instrument_data(symbol, expirationDate, strike, optionType, info=None): +def get_option_instrument_data(symbol, expirationDate, strikePrice, optionType, info=None): """Returns the option instrument data for the stock option. :param symbol: The ticker of the stock. :type symbol: str :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. :type expirationDate: str - :param strike: Represents the price of the option. - :type strike: str + :param strikePrice: Represents the price of the option. + :type strikePrice: str :param optionType: Can be either 'call' or 'put'. :type optionType: str :param info: Will filter the results to get a specific value. @@ -471,22 +438,22 @@ def get_option_instrument_data(symbol, expirationDate, strike, optionType, info= print(message) return [None] - optionID = helper.id_for_option(symbol, expirationDate, strike, optionType) + optionID = helper.id_for_option(symbol, expirationDate, strikePrice, optionType) url = urls.option_instruments(optionID) data = helper.request_get(url) return(helper.filter(data, info)) -def get_option_historicals(symbol, expirationDate, strike, optionType, interval='hour', span='week', bounds='regular', info=None): +def get_option_historicals(symbol, expirationDate, strikePrice, optionType, interval='hour', span='week', bounds='regular', info=None): """Returns the data that is used to make the graphs. :param symbol: The ticker of the stock. :type symbol: str :param expirationDate: Represents the expiration date in the format YYYY-MM-DD. :type expirationDate: str - :param strike: Represents the price of the option. - :type strike: str + :param strikePrice: Represents the price of the option. + :type strikePrice: str :param optionType: Can be either 'call' or 'put'. :type optionType: str :param interval: Interval to retrieve data for. Values are '5minute', '10minute', 'hour', 'day', 'week'. Default is 'hour'. @@ -523,7 +490,7 @@ def get_option_historicals(symbol, expirationDate, strike, optionType, interval= print('ERROR: Bounds must be "extended","regular",or "trading"') return([None]) - optionID = helper.id_for_option(symbol, expirationDate, strike, optionType) + optionID = helper.id_for_option(symbol, expirationDate, strikePrice, optionType) url = urls.option_historicals(optionID) payload = {'span': span, diff --git a/setup.py b/setup.py index 0c2cb6f9..07b4497f 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ long_description = f.read() setup(name='robin_stocks', - version='1.1.0', + version='1.2.0', description='A Python wrapper around the Robinhood API', long_description=long_description, long_description_content_type='text/markdown',