From 820375a02778b1fe5e7532bfc540a701f4150d92 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Mon, 8 Apr 2019 21:00:04 -0600 Subject: [PATCH 1/9] feature #17. stocks. symbol(s) and instrument_id(s) --- examples/stock_fetch_by_various.py | 62 ++++++++++++++++++++++++ fast_arrow/resources/stock_marketdata.py | 15 +++--- 2 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 examples/stock_fetch_by_various.py diff --git a/examples/stock_fetch_by_various.py b/examples/stock_fetch_by_various.py new file mode 100644 index 0000000..38cf773 --- /dev/null +++ b/examples/stock_fetch_by_various.py @@ -0,0 +1,62 @@ +import configparser +import csv +from fast_arrow import ( + Client, + StockMarketdata +) + +# +# get the authentication configs +# +config_file = "config.debug.ini" +config = configparser.ConfigParser() +config.read(config_file) +username = config['account']['username'] +password = config['account']['password'] + + +# +# initialize and authenticate Client +# +client = Client(username=username, password=password) +client.authenticate() + +def print_symbol_price(marketdata): + msg = "Symbol = {}. Ask price = {}. Bid price = {}".format( + marketdata['symbol'], marketdata['ask_price'], marketdata['bid_price']) + print(msg) + + +# +# fetch by single stock symbol +# +symbol = "AAPL" +md = StockMarketdata.quote_by_symbol(client, symbol) +print_symbol_price(md) + +# +# fetch by multiple stock symbols +# +symbols = ['AAPL', 'MU', 'FB'] +mds = StockMarketdata.quote_by_symbols(client, symbols) +for md in mds: + print_symbol_price(md) + +# +# fetch by single 'instrument_id' +# +symbol = 'AAPL' +md = StockMarketdata.quote_by_symbol(client, symbol) +instrument_id = md['instrument'].split('/')[4] +md = StockMarketdata.quote_by_instrument(client, instrument_id) +print_symbol_price(md) + +# +# fetch by multiple 'instrument_id's +# +symbols = ['AAPL', 'MU', 'FB'] +mds = StockMarketdata.quote_by_symbols(client, symbols) +instrument_ids = [md['instrument'].split('/')[4] for md in mds] +mds = StockMarketdata.quote_by_instruments(client, instrument_ids) +for md in mds: + print_symbol_price(md) diff --git a/fast_arrow/resources/stock_marketdata.py b/fast_arrow/resources/stock_marketdata.py index 044fa3b..16c97b5 100644 --- a/fast_arrow/resources/stock_marketdata.py +++ b/fast_arrow/resources/stock_marketdata.py @@ -8,8 +8,7 @@ def quote_by_symbol(cls, client, symbol): ''' fetch and return results ''' - return cls.quote_by_symbols(client, [symbol])['results'][0] - + return cls.quote_by_symbols(client, [symbol])[0] @classmethod def quote_by_symbols(cls, client, symbols): @@ -18,16 +17,20 @@ def quote_by_symbols(cls, client, symbols): ''' url = "https://api.robinhood.com/quotes/" params = {"symbols": ",".join(symbols)} - return client.get(url, params=params) + res = client.get(url, params=params) + return res['results'] + @classmethod + def quote_by_instrument(cls, client, _id): + return cls.quote_by_instruments(client, [_id])[0] @classmethod - def quotes_by_instrument_ids(cls, client, ids): + def quote_by_instruments(cls, client, ids): """ create instrument urls, fetch, return results """ - base_url = "https://api.robinhood.com/instruments/" - id_urls = ["{}{}/".format(base_url, _id) for _id in ids] + base_url = "https://api.robinhood.com/instruments" + id_urls = ["{}/{}/".format(base_url, _id) for _id in ids] return cls.quotes_by_instrument_urls(client, id_urls) From 3593b335b60cf1d93efb3a29caefd3b125637df0 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Mon, 8 Apr 2019 21:03:19 -0600 Subject: [PATCH 2/9] feature #17. bump version because I changed api. --- fast_arrow/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fast_arrow/version.py b/fast_arrow/version.py index d325e71..64a0641 100644 --- a/fast_arrow/version.py +++ b/fast_arrow/version.py @@ -1 +1 @@ -VERSION = '0.3.0' +VERSION = '0.3.1' From 3cb401c54425997fbb1bccb82611c2107a7020f8 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 14:56:02 -0600 Subject: [PATCH 3/9] feature #17. add deprecation python module. --- Pipfile | 1 + Pipfile.lock | 30 ++++++++++++++++++++++++++---- setup.py | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Pipfile b/Pipfile index dd5e75b..29b2a3f 100644 --- a/Pipfile +++ b/Pipfile @@ -11,6 +11,7 @@ yarl = "*" pandas = ">=0.23.2" numpy = "*" datetime = "*" +deprecation = "*" [dev-packages] vcrpy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index bca32fe..d40b394 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "9ae5c4ccfbbbbf9fab572bf02d3b5d9e335759a6f9cc26ae9f88c55a8a6a8330" + "sha256": "e2adfed744e7620e5e4f24031673aaa2b35e475dc248422a3579e2a2aef19eef" }, "pipfile-spec": 6, "requires": { @@ -38,6 +38,14 @@ "index": "pypi", "version": "==4.3" }, + "deprecation": { + "hashes": [ + "sha256:68071e5ae7cd7e9da6c7dffd750922be4825c7c3a6780d29314076009cc39c35", + "sha256:fecd0f05024126466ba7e5309b905f09fce7d25d67e4648f7ec5488f9e764310" + ], + "index": "pypi", + "version": "==2.0.6" + }, "fast-arrow": { "editable": true, "path": "." @@ -112,6 +120,13 @@ "index": "pypi", "version": "==1.16.2" }, + "packaging": { + "hashes": [ + "sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", + "sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3" + ], + "version": "==19.0" + }, "pandas": { "hashes": [ "sha256:071e42b89b57baa17031af8c6b6bbd2e9a5c68c595bc6bf9adabd7a9ed125d3b", @@ -146,6 +161,13 @@ "index": "pypi", "version": "==2.3.3" }, + "pyparsing": { + "hashes": [ + "sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", + "sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03" + ], + "version": "==2.4.0" + }, "python-dateutil": { "hashes": [ "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", @@ -155,10 +177,10 @@ }, "pytz": { "hashes": [ - "sha256:32b0891edff07e28efe91284ed9c31e123d84bea3fd98e1f72be2508f43ef8d9", - "sha256:d5f05e487007e29e03409f9398d074e158d920d36eb82eaf66fb1136b0c5374c" + "sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", + "sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141" ], - "version": "==2018.9" + "version": "==2019.1" }, "requests": { "hashes": [ diff --git a/setup.py b/setup.py index cc07091..cfc9833 100644 --- a/setup.py +++ b/setup.py @@ -29,6 +29,7 @@ def run_tests(self): deps = [ 'datetime', + 'deprecation', 'pathlib2', 'requests>=2.20.0', 'pandas>=0.23.2', From 0d11f78b0e923eb52c7e207976226de2d0ef232a Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 14:56:36 -0600 Subject: [PATCH 4/9] feature #17. turn on deprecation warnings for fast_arrow. --- fast_arrow/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fast_arrow/__init__.py b/fast_arrow/__init__.py index 9b0677d..9b1e26f 100644 --- a/fast_arrow/__init__.py +++ b/fast_arrow/__init__.py @@ -34,3 +34,6 @@ # dividend from fast_arrow.resources.dividend import Dividend + +import warnings +warnings.simplefilter('always', DeprecationWarning) From 2a08fab980bf2c53dafa2784265bfeb1c510cd04 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 14:57:18 -0600 Subject: [PATCH 5/9] feature #17. fetch historical stock data by symbol or symbols. --- fast_arrow/resources/stock_marketdata.py | 34 +++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/fast_arrow/resources/stock_marketdata.py b/fast_arrow/resources/stock_marketdata.py index 16c97b5..31e0456 100644 --- a/fast_arrow/resources/stock_marketdata.py +++ b/fast_arrow/resources/stock_marketdata.py @@ -1,4 +1,7 @@ from fast_arrow.util import chunked_list +import deprecation +from fast_arrow.version import VERSION + class StockMarketdata(object): @@ -50,7 +53,19 @@ def quotes_by_instrument_urls(cls, client, urls): return results @classmethod + @deprecation.deprecated( + deprecated_in="0.3.1", removed_in="0.4",current_version=VERSION, + details="Use 'historical_quote_by_symbol' instead") def historical(cls, client, symbol, span="year", bounds="regular"): + return cls.historical_quote_by_symbol(client, symbol, span, bounds) + + @classmethod + def historical_quote_by_symbol(cls, client, symbols, span="year", bounds="regular"): + datas = cls.historical_quote_by_symbols(client, [symbol],span, bounds) + return datas[0] + + @classmethod + def historical_quote_by_symbols(cls, client, symbols, span="year", bounds="regular"): possible_intervals = { "day": "5minute", "week": "10minute", @@ -60,12 +75,13 @@ def historical(cls, client, symbol, span="year", bounds="regular"): interval = possible_intervals[span] assert bounds in ["regular", "trading"] - request_url = "https://api.robinhood.com/marketdata/historicals/{}/".format(symbol) - params = { - "span": span, - "interval": interval, - "bounds": bounds, - "symbol": symbol - } - data = client.get(request_url, params=params) - return data + request_url = "https://api.robinhood.com/marketdata/historicals/" + + results = [] + for _symbols in chunked_list(symbols, 25): + params = { "span": span, "interval": interval, "bounds": bounds, + "symbols": ",".join(_symbols) } + data = client.get(request_url, params=params) + if data and data["results"]: + results.extend(data["results"]) + return results From ca4f6319bc4fcce6fb98f86675b6ea372e6ab8f7 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 15:50:23 -0600 Subject: [PATCH 6/9] add print line to examples. --- examples/auth.py | 48 +++++++------ examples/auth_mfa.py | 50 ++++++------- examples/historical_option_data.py | 4 +- examples/option_chain.py | 1 + examples/option_events.py | 1 + examples/option_order_get.py | 1 + examples/option_order_place_iron_condor.py | 70 +++++++++---------- examples/option_order_place_single.py | 1 + examples/option_order_place_vertical.py | 1 + examples/option_order_replace.py | 1 + examples/option_orders_max_page.py | 3 + examples/option_orders_unroll.py | 6 +- examples/option_positions.py | 2 + examples/option_positions_humanized.py | 4 +- examples/option_positions_max_date.py | 2 + examples/portfolio_historicals.py | 17 ++++- examples/stock_historical_export.py | 4 ++ examples/stock_positions.py | 4 ++ ...ous.py => stock_quotes_various_methods.py} | 3 + examples/stocks_by_collection.py | 4 ++ .../graph-TLT-option-interest.ipynb | 0 ...h-delta-vs-theta-for-optionpositions.ipynb | 0 fast_arrow/__init__.py | 3 +- fast_arrow/resources/portfolio.py | 4 +- fast_arrow/resources/stock.py | 2 +- fast_arrow/resources/stock_marketdata.py | 2 +- run_all_examples.sh | 2 +- 27 files changed, 144 insertions(+), 96 deletions(-) rename examples/{stock_fetch_by_various.py => stock_quotes_various_methods.py} (97%) rename {examples => examples_ipython}/graph-TLT-option-interest.ipynb (100%) rename {examples => examples_ipython}/graph-delta-vs-theta-for-optionpositions.ipynb (100%) mode change 100644 => 100755 run_all_examples.sh diff --git a/examples/auth.py b/examples/auth.py index e1b9dc8..12bb210 100644 --- a/examples/auth.py +++ b/examples/auth.py @@ -2,28 +2,30 @@ from fast_arrow import Client from fast_arrow.resources.user import User +print("----- running {}".format(__file__)) + +# # +# # get the authentication configs +# # +# config_file = "config.debug.ini" +# config = configparser.ConfigParser() +# config.read(config_file) +# username = config['account']['username'] +# password = config['account']['password'] # -# get the authentication configs +# client = Client(username=username, password=password) +# result = client.authenticate() # -config_file = "config.debug.ini" -config = configparser.ConfigParser() -config.read(config_file) -username = config['account']['username'] -password = config['account']['password'] - -client = Client(username=username, password=password) -result = client.authenticate() - -print("Authenticated successfully = {}".format(result)) - -print("Account Url = {}".format(client.account_url)) -print("Account Id = {}".format(client.account_id)) - -user = User.fetch(client) -print("Username = {}".format(user["username"])) - -result = client.relogin_oauth2() -print("Re-Authenticated with refresh_token successfully = {}".format(result)) - -result = client.logout_oauth2() -print("Logged out successfully = {}".format(result)) +# print("Authenticated successfully = {}".format(result)) +# +# print("Account Url = {}".format(client.account_url)) +# print("Account Id = {}".format(client.account_id)) +# +# user = User.fetch(client) +# print("Username = {}".format(user["username"])) +# +# result = client.relogin_oauth2() +# print("Re-Authenticated with refresh_token successfully = {}".format(result)) +# +# result = client.logout_oauth2() +# print("Logged out successfully = {}".format(result)) diff --git a/examples/auth_mfa.py b/examples/auth_mfa.py index 7ff0335..2c2182d 100644 --- a/examples/auth_mfa.py +++ b/examples/auth_mfa.py @@ -2,30 +2,32 @@ from fast_arrow import Client from fast_arrow.resources.user import User +print("----- running {}".format(__file__)) + # # get the authentication configs # -config_file = "config.debug.ini" -config = configparser.ConfigParser() -config.read(config_file) -username = config['account']['username'] -password = config['account']['password'] - -mfa_code = "code goes here as a string" - -client = Client(username=username, password=password, mfa_code=mfa_code) -result = client.authenticate() - -print("Authenticated successfully = {}".format(result)) - -print("Account Url = {}".format(client.account_url)) -print("Account Id = {}".format(client.account_id)) - -user = User.fetch(client) -print("Username = {}".format(user["username"])) - -result = client.relogin_oauth2() -print("Re-Authenticated with refresh_token successfully = {}".format(result)) - -result = client.logout_oauth2() -print("Logged out successfully = {}".format(result)) +# config_file = "config.debug.ini" +# config = configparser.ConfigParser() +# config.read(config_file) +# username = config['account']['username'] +# password = config['account']['password'] +# +# mfa_code = "code goes here as a string" +# +# client = Client(username=username, password=password, mfa_code=mfa_code) +# result = client.authenticate() +# +# print("Authenticated successfully = {}".format(result)) +# +# print("Account Url = {}".format(client.account_url)) +# print("Account Id = {}".format(client.account_id)) +# +# user = User.fetch(client) +# print("Username = {}".format(user["username"])) +# +# result = client.relogin_oauth2() +# print("Re-Authenticated with refresh_token successfully = {}".format(result)) +# +# result = client.logout_oauth2() +# print("Logged out successfully = {}".format(result)) diff --git a/examples/historical_option_data.py b/examples/historical_option_data.py index d344d29..8a954b3 100644 --- a/examples/historical_option_data.py +++ b/examples/historical_option_data.py @@ -6,6 +6,9 @@ OptionChain, OptionMarketdata ) +import math + +print("----- running {}".format(__file__)) # # get the authentication configs @@ -38,7 +41,6 @@ # get TLT in the middle of the current TLT trading range # urls = [op["url"] for op in ops] -import math middle = math.floor(len(urls)/2) diff = math.floor(len(urls) * 0.7) lower_end = middle - diff diff --git a/examples/option_chain.py b/examples/option_chain.py index b84793e..4c9d28a 100644 --- a/examples/option_chain.py +++ b/examples/option_chain.py @@ -6,6 +6,7 @@ Option, ) +print("----- running {}".format(__file__)) # # get the authentication configs diff --git a/examples/option_events.py b/examples/option_events.py index ecd8629..9684b57 100644 --- a/examples/option_events.py +++ b/examples/option_events.py @@ -4,6 +4,7 @@ OptionEvent ) +print("----- running {}".format(__file__)) # # get the authentication configs diff --git a/examples/option_order_get.py b/examples/option_order_get.py index a7ebcb2..0c879b9 100644 --- a/examples/option_order_get.py +++ b/examples/option_order_get.py @@ -9,6 +9,7 @@ ) import math +print("----- running {}".format(__file__)) # # get the authentication configs diff --git a/examples/option_order_place_iron_condor.py b/examples/option_order_place_iron_condor.py index 71db688..3a07053 100644 --- a/examples/option_order_place_iron_condor.py +++ b/examples/option_order_place_iron_condor.py @@ -13,6 +13,8 @@ Stock ) +print("----- running {}".format(__file__)) + # # get the authentication configs # @@ -35,7 +37,6 @@ # symbol = "SPY" stock = Stock.fetch(client, symbol) -stock = Stock.mergein_marketdata_list(client, [stock])[0] oc = OptionChain.fetch(client, stock["id"], symbol) ed = oc['expiration_dates'][10] @@ -53,39 +54,38 @@ width = 1 put_inner_lte_delta = -0.2 call_inner_lte_delta = 0.1 -ic = IronCondor.generate_by_deltas(ops, - width, put_inner_lte_delta, call_inner_lte_delta) - -direction = "credit" -legs = ic["legs"] -# @TODO create helper methods to handle floating arith and rounding issues -# for now, it works good enough -price_notional = ic["price"] * 100 -price_notional_fourth = price_notional / 4 -price_order = price_notional_fourth / 100 -price = str(price_order) - -quantity = 1 -time_in_force = "gfd" -trigger = "immediate" -order_type = "limit" - -# @TODO create human description of IC -# print("Selling a {} {}/{} Put Spread for {} (notional value = ${})".format( -# symbol, -# vertical["strike_price"].values[0], -# vertical["strike_price_shifted"].values[0], -# price, -# my_bid_price_rounded) -# ) - -oo = OptionOrder.submit(client, direction, legs, price, quantity, time_in_force, trigger, order_type) - -print("Order submitted ... ref_id = {}".format(oo["ref_id"])) - +# ic = IronCondor.generate_by_deltas(ops,width, put_inner_lte_delta, call_inner_lte_delta) +# +# direction = "credit" +# legs = ic["legs"] +# # @TODO create helper methods to handle floating arith and rounding issues +# # for now, it works good enough +# price_notional = ic["price"] * 100 +# price_notional_fourth = price_notional / 4 +# price_order = price_notional_fourth / 100 +# price = str(price_order) +# +# quantity = 1 +# time_in_force = "gfd" +# trigger = "immediate" +# order_type = "limit" +# +# # @TODO create human description of IC +# # print("Selling a {} {}/{} Put Spread for {} (notional value = ${})".format( +# # symbol, +# # vertical["strike_price"].values[0], +# # vertical["strike_price_shifted"].values[0], +# # price, +# # my_bid_price_rounded) +# # ) +# +# oo = OptionOrder.submit(client, direction, legs, price, quantity, time_in_force, trigger, order_type) # -# cancel the order +# print("Order submitted ... ref_id = {}".format(oo["ref_id"])) # -print("Canceling order = {}".format(oo["ref_id"])) -result = OptionOrder.cancel(client, oo['cancel_url']) -print("Order canceled result = {}".format(result)) +# # +# # cancel the order +# # +# print("Canceling order = {}".format(oo["ref_id"])) +# result = OptionOrder.cancel(client, oo['cancel_url']) +# print("Order canceled result = {}".format(result)) diff --git a/examples/option_order_place_single.py b/examples/option_order_place_single.py index ec1070b..eb966c9 100644 --- a/examples/option_order_place_single.py +++ b/examples/option_order_place_single.py @@ -8,6 +8,7 @@ ) import math +print("----- running {}".format(__file__)) # # get the authentication configs diff --git a/examples/option_order_place_vertical.py b/examples/option_order_place_vertical.py index 58acb43..74d74b5 100644 --- a/examples/option_order_place_vertical.py +++ b/examples/option_order_place_vertical.py @@ -9,6 +9,7 @@ ) import math +print("----- running {}".format(__file__)) # # get the authentication configs diff --git a/examples/option_order_replace.py b/examples/option_order_replace.py index c4ad6a6..45cdca7 100644 --- a/examples/option_order_replace.py +++ b/examples/option_order_replace.py @@ -9,6 +9,7 @@ ) import math +print("----- running {}".format(__file__)) # # get the authentication configs diff --git a/examples/option_orders_max_page.py b/examples/option_orders_max_page.py index 256d208..a9ed4d6 100644 --- a/examples/option_orders_max_page.py +++ b/examples/option_orders_max_page.py @@ -3,6 +3,9 @@ from datetime import datetime, timedelta +print("----- running {}".format(__file__)) + + config = configparser.ConfigParser() config.read('config.debug.ini') diff --git a/examples/option_orders_unroll.py b/examples/option_orders_unroll.py index d3ab1cc..4f7a2cd 100644 --- a/examples/option_orders_unroll.py +++ b/examples/option_orders_unroll.py @@ -2,6 +2,9 @@ from fast_arrow import Client, OptionOrder +print("----- running {}".format(__file__)) + + config = configparser.ConfigParser() config.read('config.debug.ini') @@ -34,8 +37,7 @@ # option_orders_unrolled = OptionOrder.unroll_option_legs(client, option_orders) - # # let's print out the results # -print(option_orders_unrolled[1:10]) +print(option_orders_unrolled[0].keys()) diff --git a/examples/option_positions.py b/examples/option_positions.py index 56c4d11..5d64e36 100644 --- a/examples/option_positions.py +++ b/examples/option_positions.py @@ -6,6 +6,8 @@ OptionPosition ) +print("----- running {}".format(__file__)) + # # get the authentication configs # diff --git a/examples/option_positions_humanized.py b/examples/option_positions_humanized.py index fb11d9b..361d827 100644 --- a/examples/option_positions_humanized.py +++ b/examples/option_positions_humanized.py @@ -9,6 +9,8 @@ OptionMarketdata, ) +print("----- running {}".format(__file__)) + # # get the authentication configs # @@ -73,5 +75,5 @@ # create Pandas DF of option positions # df = pd.DataFrame.from_dict(ops) -# +# print(df) diff --git a/examples/option_positions_max_date.py b/examples/option_positions_max_date.py index e99659a..5181c06 100644 --- a/examples/option_positions_max_date.py +++ b/examples/option_positions_max_date.py @@ -7,6 +7,8 @@ OptionPosition ) +print("----- running {}".format(__file__)) + # # get the authentication configs # diff --git a/examples/portfolio_historicals.py b/examples/portfolio_historicals.py index ccde5ad..419e2ed 100644 --- a/examples/portfolio_historicals.py +++ b/examples/portfolio_historicals.py @@ -1,6 +1,11 @@ import configparser from fast_arrow import Client from fast_arrow import Portfolio +from fast_arrow import Account + + +print("----- running {}".format(__file__)) + # # get the authentication configs @@ -10,19 +15,25 @@ config.read(config_file) username = config['account']['username'] password = config['account']['password'] -account = config['account']['account'] + client = Client(username=username, password=password) result = client.authenticate() + +account = Account.all(client)[0] +account_number = account['account_number'] + + span="year" bounds="regular" -portfolio_historicals = Portfolio.historical(client, account, span, bounds) +portfolio_historicals = Portfolio.historical(client, account_number, span, bounds) + ehs = portfolio_historicals["equity_historicals"] begins_at = ehs[-1]["begins_at"] ends_at = ehs[0]["begins_at"] -print(ehs[0].keys()) +print(ehs[0].keys()) print("Fetched portfolio data between {} and {}".format(begins_at, ends_at)) diff --git a/examples/stock_historical_export.py b/examples/stock_historical_export.py index e69ae4d..9dc3296 100644 --- a/examples/stock_historical_export.py +++ b/examples/stock_historical_export.py @@ -5,6 +5,10 @@ StockMarketdata ) + +print("----- running {}".format(__file__)) + + # # get the authentication configs # diff --git a/examples/stock_positions.py b/examples/stock_positions.py index e75cada..55d8316 100644 --- a/examples/stock_positions.py +++ b/examples/stock_positions.py @@ -4,6 +4,10 @@ StockPosition ) + +print("----- running {}".format(__file__)) + + # # get the authentication configs # diff --git a/examples/stock_fetch_by_various.py b/examples/stock_quotes_various_methods.py similarity index 97% rename from examples/stock_fetch_by_various.py rename to examples/stock_quotes_various_methods.py index 38cf773..4abcf43 100644 --- a/examples/stock_fetch_by_various.py +++ b/examples/stock_quotes_various_methods.py @@ -5,6 +5,9 @@ StockMarketdata ) +print("----- running {}".format(__file__)) + + # # get the authentication configs # diff --git a/examples/stocks_by_collection.py b/examples/stocks_by_collection.py index dcdadaf..2c4eb32 100644 --- a/examples/stocks_by_collection.py +++ b/examples/stocks_by_collection.py @@ -4,6 +4,10 @@ Collection ) + +print("----- running {}".format(__file__)) + + # # get the authentication configs # diff --git a/examples/graph-TLT-option-interest.ipynb b/examples_ipython/graph-TLT-option-interest.ipynb similarity index 100% rename from examples/graph-TLT-option-interest.ipynb rename to examples_ipython/graph-TLT-option-interest.ipynb diff --git a/examples/graph-delta-vs-theta-for-optionpositions.ipynb b/examples_ipython/graph-delta-vs-theta-for-optionpositions.ipynb similarity index 100% rename from examples/graph-delta-vs-theta-for-optionpositions.ipynb rename to examples_ipython/graph-delta-vs-theta-for-optionpositions.ipynb diff --git a/fast_arrow/__init__.py b/fast_arrow/__init__.py index 9b1e26f..5a93af9 100644 --- a/fast_arrow/__init__.py +++ b/fast_arrow/__init__.py @@ -25,8 +25,7 @@ # user from fast_arrow.resources.user import User - -# portfolio +from fast_arrow.resources.account import Account from fast_arrow.resources.portfolio import Portfolio # search diff --git a/fast_arrow/resources/portfolio.py b/fast_arrow/resources/portfolio.py index 080b6c4..d16c81d 100644 --- a/fast_arrow/resources/portfolio.py +++ b/fast_arrow/resources/portfolio.py @@ -11,8 +11,8 @@ def historical(cls, client, account="", span="year", bounds="regular"): interval = possible_intervals[span] assert bounds in ["regular", "trading"] - base_request_url = "https://api.robinhood.com/portfolios/historicals/" - request_url = "{}{}/".format(base_request_url, account) + base_request_url = "https://api.robinhood.com/portfolios/historicals" + request_url = "{}/{}/".format(base_request_url, account) params = { "span": span, "interval": interval, "bounds": bounds } data = client.get(request_url, params=params) return data diff --git a/fast_arrow/resources/stock.py b/fast_arrow/resources/stock.py index c2e9cfa..54bc981 100644 --- a/fast_arrow/resources/stock.py +++ b/fast_arrow/resources/stock.py @@ -16,7 +16,7 @@ def fetch(cls, client, symbol): @classmethod def mergein_marketdata_list(cls, client, stocks): ids = [x["id"] for x in stocks] - mds = StockMarketdata.quotes_by_instrument_ids(client, ids) + mds = StockMarketdata.quote_by_instruments(client, ids) mds = [x for x in mds if x] results = [] diff --git a/fast_arrow/resources/stock_marketdata.py b/fast_arrow/resources/stock_marketdata.py index 31e0456..0480474 100644 --- a/fast_arrow/resources/stock_marketdata.py +++ b/fast_arrow/resources/stock_marketdata.py @@ -60,7 +60,7 @@ def historical(cls, client, symbol, span="year", bounds="regular"): return cls.historical_quote_by_symbol(client, symbol, span, bounds) @classmethod - def historical_quote_by_symbol(cls, client, symbols, span="year", bounds="regular"): + def historical_quote_by_symbol(cls, client, symbol, span="year", bounds="regular"): datas = cls.historical_quote_by_symbols(client, [symbol],span, bounds) return datas[0] diff --git a/run_all_examples.sh b/run_all_examples.sh old mode 100644 new mode 100755 index 91b2809..4cafbe6 --- a/run_all_examples.sh +++ b/run_all_examples.sh @@ -1 +1 @@ -for f in examples/*.py; do python "$f"; done +for f in examples/*.py; do sleep 2s && python "$f"; done From 95c7a0b376915a778b9a56996da3dd91ac78fce2 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 15:55:27 -0600 Subject: [PATCH 7/9] example upkeep. provide a pipenv script to run all of them. sleep for 5 seconds in between examples so we don't get a HTTP 429 "too many requests" from RH's API --- Makefile | 5 ++++ Pipfile | 3 +++ examples/auth.py | 46 ++++++++++++++++++------------------- examples/auth_mfa.py | 54 ++++++++++++++++++++++---------------------- run_all_examples.sh | 2 +- 5 files changed, 59 insertions(+), 51 deletions(-) diff --git a/Makefile b/Makefile index a6d7b6a..0442dbe 100644 --- a/Makefile +++ b/Makefile @@ -13,3 +13,8 @@ coveralls: .PHONY: lint lint: pipenv run tox -e lint + + +.PHONY: examples +examples: + pipenv run run_all_examples diff --git a/Pipfile b/Pipfile index 29b2a3f..d9f1ac6 100644 --- a/Pipfile +++ b/Pipfile @@ -24,3 +24,6 @@ ipdb = "*" [requires] python_version = "3.6" + +[scripts] +run_all_examples = "sh ./run_all_examples.sh" diff --git a/examples/auth.py b/examples/auth.py index 12bb210..a27734d 100644 --- a/examples/auth.py +++ b/examples/auth.py @@ -4,28 +4,28 @@ print("----- running {}".format(__file__)) -# # -# # get the authentication configs -# # -# config_file = "config.debug.ini" -# config = configparser.ConfigParser() -# config.read(config_file) -# username = config['account']['username'] -# password = config['account']['password'] # -# client = Client(username=username, password=password) -# result = client.authenticate() +# get the authentication configs # -# print("Authenticated successfully = {}".format(result)) -# -# print("Account Url = {}".format(client.account_url)) -# print("Account Id = {}".format(client.account_id)) -# -# user = User.fetch(client) -# print("Username = {}".format(user["username"])) -# -# result = client.relogin_oauth2() -# print("Re-Authenticated with refresh_token successfully = {}".format(result)) -# -# result = client.logout_oauth2() -# print("Logged out successfully = {}".format(result)) +config_file = "config.debug.ini" +config = configparser.ConfigParser() +config.read(config_file) +username = config['account']['username'] +password = config['account']['password'] + +client = Client(username=username, password=password) +result = client.authenticate() + +print("Authenticated successfully = {}".format(result)) + +print("Account Url = {}".format(client.account_url)) +print("Account Id = {}".format(client.account_id)) + +user = User.fetch(client) +print("Username = {}".format(user["username"])) + +result = client.relogin_oauth2() +print("Re-Authenticated with refresh_token successfully = {}".format(result)) + +result = client.logout_oauth2() +print("Logged out successfully = {}".format(result)) diff --git a/examples/auth_mfa.py b/examples/auth_mfa.py index 2c2182d..73925fa 100644 --- a/examples/auth_mfa.py +++ b/examples/auth_mfa.py @@ -4,30 +4,30 @@ print("----- running {}".format(__file__)) -# -# get the authentication configs -# -# config_file = "config.debug.ini" -# config = configparser.ConfigParser() -# config.read(config_file) -# username = config['account']['username'] -# password = config['account']['password'] -# -# mfa_code = "code goes here as a string" -# -# client = Client(username=username, password=password, mfa_code=mfa_code) -# result = client.authenticate() -# -# print("Authenticated successfully = {}".format(result)) -# -# print("Account Url = {}".format(client.account_url)) -# print("Account Id = {}".format(client.account_id)) -# -# user = User.fetch(client) -# print("Username = {}".format(user["username"])) -# -# result = client.relogin_oauth2() -# print("Re-Authenticated with refresh_token successfully = {}".format(result)) -# -# result = client.logout_oauth2() -# print("Logged out successfully = {}".format(result)) + +get the authentication configs + +config_file = "config.debug.ini" +config = configparser.ConfigParser() +config.read(config_file) +username = config['account']['username'] +password = config['account']['password'] + +mfa_code = "code goes here as a string" + +client = Client(username=username, password=password, mfa_code=mfa_code) +result = client.authenticate() + +print("Authenticated successfully = {}".format(result)) + +print("Account Url = {}".format(client.account_url)) +print("Account Id = {}".format(client.account_id)) + +user = User.fetch(client) +print("Username = {}".format(user["username"])) + +result = client.relogin_oauth2() +print("Re-Authenticated with refresh_token successfully = {}".format(result)) + +result = client.logout_oauth2() +print("Logged out successfully = {}".format(result)) diff --git a/run_all_examples.sh b/run_all_examples.sh index 4cafbe6..872903c 100755 --- a/run_all_examples.sh +++ b/run_all_examples.sh @@ -1 +1 @@ -for f in examples/*.py; do sleep 2s && python "$f"; done +for f in examples/*.py; do sleep 5s && python "$f"; done From df96be7613eb42c8bedf60a474594b6b44afe124 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 15:56:13 -0600 Subject: [PATCH 8/9] pull unauthenticated Stock quote endpoints. they don't work. --- examples/stock_quote.py | 18 ------------------ fast_arrow/resources/stock_marketdata.py | 17 ----------------- 2 files changed, 35 deletions(-) delete mode 100644 examples/stock_quote.py diff --git a/examples/stock_quote.py b/examples/stock_quote.py deleted file mode 100644 index 175d615..0000000 --- a/examples/stock_quote.py +++ /dev/null @@ -1,18 +0,0 @@ -from fast_arrow import ( - Client, - StockMarketdata -) - -# -# initialize. Don't need to authenticate for stock data. -# -client = Client() - -# -# fetch price quote data -# -symbol = "SQ" -stock = StockMarketdata.quote_by_symbol(client, symbol) - -print("{} ask_price = {}".format(stock['symbol'], stock['ask_price'])) -print("{} bid_price = {}".format(stock['symbol'], stock['bid_price'])) diff --git a/fast_arrow/resources/stock_marketdata.py b/fast_arrow/resources/stock_marketdata.py index 0480474..020fa3e 100644 --- a/fast_arrow/resources/stock_marketdata.py +++ b/fast_arrow/resources/stock_marketdata.py @@ -6,23 +6,6 @@ class StockMarketdata(object): - @classmethod - def quote_by_symbol(cls, client, symbol): - ''' - fetch and return results - ''' - return cls.quote_by_symbols(client, [symbol])[0] - - @classmethod - def quote_by_symbols(cls, client, symbols): - ''' - fetch and return results - ''' - url = "https://api.robinhood.com/quotes/" - params = {"symbols": ",".join(symbols)} - res = client.get(url, params=params) - return res['results'] - @classmethod def quote_by_instrument(cls, client, _id): return cls.quote_by_instruments(client, [_id])[0] From 3b33d24b1953cc3b7d6d4776525625976cb56592 Mon Sep 17 00:00:00 2001 From: westonplatter Date: Sat, 13 Apr 2019 15:56:33 -0600 Subject: [PATCH 9/9] code formatting. no changes. --- fast_arrow/client.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fast_arrow/client.py b/fast_arrow/client.py index 0b1e27c..8fc8383 100644 --- a/fast_arrow/client.py +++ b/fast_arrow/client.py @@ -68,7 +68,8 @@ def post(self, url=None, payload=None, retry=True): attempts = 1 while attempts <= HTTP_ATTEMPTS_MAX: try: - res = requests.post(url, headers=headers, data=payload, timeout=15, verify=self.certs) + res = requests.post(url, headers=headers, data=payload, + timeout=15, verify=self.certs) res.raise_for_status() if res.headers['Content-Length'] == '0': return None @@ -76,7 +77,7 @@ def post(self, url=None, payload=None, retry=True): return res.json() except requests.exceptions.RequestException as e: attempts += 1 - if res.status_code in [400]: + if res.status_code in [400, 429]: raise e elif retry and res.status_code in [403]: self.relogin_oauth2()