From 96c937bb0cef32a88b4881941c86ac388afa7486 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 21 Aug 2019 14:05:28 -0400 Subject: [PATCH 1/6] Added exporting dividends with the --dividends flag --- csv-export.py | 93 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 6 deletions(-) diff --git a/csv-export.py b/csv-export.py index b8b214a..21c09bb 100644 --- a/csv-export.py +++ b/csv-export.py @@ -22,6 +22,8 @@ '--mfa_code', help='your Robinhood mfa_code') parser.add_argument( '--profit', action='store_true', help='calculate profit for each sale') +parser.add_argument( + '--dividends', action='store_true', help='export dividend payments') args = parser.parse_args() username = args.username password = args.password @@ -170,12 +172,91 @@ if filename == '': filename = "robinhood.csv" -# save the CSV -try: - with open(filename, "w+") as outfile: - outfile.write(csv) -except IOError: - print("Oops. Unable to write file to ", filename) + +if args.dividends: + fields=collections.defaultdict(dict) + dividend_count = 0 + queued_dividends = 0 + + # fetch order history and related metadata from the Robinhood API + dividends = robinhood.get_endpoint('dividends') + + + paginated = True + page = 0 + while paginated: + for i, dividend in enumerate(dividends['results']): + symbol = cached_instruments.get(dividend['instrument'], False) + if not symbol: + symbol = robinhood.get_custom_endpoint(dividend['instrument'])['symbol'] + cached_instruments[dividend['instrument']] = symbol + + fields[i + (page * 100)]['symbol'] = symbol + + for key, value in enumerate(dividend): + if value != "executions": + fields[i + (page * 100)][value] = dividend[value] + + fields[i + (page * 100)]['execution_state'] = order['state'] + + if dividend['state'] == "pending": + queued_dividends += 1 + elif dividend['state'] == "paid": + dividend_count += 1 + # paginate + if dividends['next'] is not None: + page = page + 1 + orders = robinhood.get_custom_endpoint(str(dividends['next'])) + else: + paginated = False + + # for i in fields: + # print fields[i] + # print "-------" + + # check we have trade data to export + if dividend_count > 0 or queued_dividends > 0: + print("%d queued dividend%s and %d executed dividend%s found in your account." % + (queued_dividends, "s" [queued_count == 1:], dividend_count, + "s" [trade_count == 1:])) + # print str(queued_count) + " queded trade(s) and " + str(trade_count) + " executed trade(s) found in your account." + else: + print("No dividend history found in your account.") + quit() + + # CSV headers + keys = fields[0].keys() + keys = sorted(keys) + csv = ','.join(keys) + "\n" + + # CSV rows + for row in fields: + for idx, key in enumerate(keys): + if (idx > 0): + csv += "," + try: + csv += str(fields[row][key]) + except: + csv += "" + + csv += "\n" + + # choose a filename to save to + print("Choose a filename or press enter to save to `dividends.csv`:") + try: + input = raw_input + except NameError: + pass + filename = input().strip() + if filename == '': + filename = "dividends.csv" + + # save the CSV + try: + with open(filename, "w+") as outfile: + outfile.write(csv) + except IOError: + print("Oops. Unable to write file to ", filename) if args.profit: profit_csv = profit_extractor(csv, filename) From 79e8df7073f706f76c1537c5b1b3335d737e22f6 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 21 Aug 2019 14:08:32 -0400 Subject: [PATCH 2/6] Fixed accidental deletion of order output --- csv-export.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/csv-export.py b/csv-export.py index 21c09bb..02a3c2a 100644 --- a/csv-export.py +++ b/csv-export.py @@ -172,6 +172,12 @@ if filename == '': filename = "robinhood.csv" +try: + with open(filename, "w+") as outfile: + otufile.write(csv) +except IOError: + print("Oops. Unable to write file to ", filename) + if args.dividends: fields=collections.defaultdict(dict) From 36dd647469076c35c39108a8f2d42a0039b1c107 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 21 Aug 2019 14:10:05 -0400 Subject: [PATCH 3/6] Fixed Typo --- csv-export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csv-export.py b/csv-export.py index 02a3c2a..606e373 100644 --- a/csv-export.py +++ b/csv-export.py @@ -174,7 +174,7 @@ try: with open(filename, "w+") as outfile: - otufile.write(csv) + outfile.write(csv) except IOError: print("Oops. Unable to write file to ", filename) From 4647d2728b7c37733c73f5f1e9203be2d973b293 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 21 Aug 2019 15:04:56 -0400 Subject: [PATCH 4/6] Made changes to make more compatible with Python2.7 as well as fixing some login issues. --- csv-options-export.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/csv-options-export.py b/csv-options-export.py index 6162df5..8ba1093 100644 --- a/csv-options-export.py +++ b/csv-options-export.py @@ -1,3 +1,4 @@ +from __future__ import print_function from Robinhood import Robinhood from profit_extractor import profit_extractor import getpass @@ -34,7 +35,7 @@ while logged_in != True: if username == "": - username = os.getenv("RH_USERNAME") + username = os.getenv("RH_USERNAME", "") if username == "": print("Robinhood username:", end=' ') try: @@ -44,12 +45,12 @@ username = input() if password == "": - password = os.getenv("RH_PASSWORD") + password = os.getenv("RH_PASSWORD", "") if password == "": password = getpass.getpass() logged_in = robinhood.login(username=username, password=password) - if logged_in != True and logged_in.get('non_field_errors') == None and logged_in['mfa_required'] == True: + if logged_in != True and logged_in.get('non_field_errors') == None and logged_in.get('mfa_required') == True: mfa_code = os.getenv("RH_MFA") if mfa_code == "": print("Robinhood MFA:", end=' ') From 37de9f6025116851d59c9f6edf0ec284e7f0e279 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 21 Aug 2019 15:54:53 -0400 Subject: [PATCH 5/6] Made changes to support specification of date ranges to csv-export.py --- csv-export.py | 124 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 41 deletions(-) diff --git a/csv-export.py b/csv-export.py index 606e373..0379c5f 100644 --- a/csv-export.py +++ b/csv-export.py @@ -7,6 +7,7 @@ import ast from dotenv import load_dotenv, find_dotenv import os +import datetime logged_in = False @@ -24,10 +25,26 @@ '--profit', action='store_true', help='calculate profit for each sale') parser.add_argument( '--dividends', action='store_true', help='export dividend payments') +parser.add_argument( + '--start_date', default='', help='The start date of transactions logged Format: YYYY-MM-DD') +parser.add_argument( + '--end_date', default='', help='The end date of transactions logged Format: YYYY-MM-DD') args = parser.parse_args() username = args.username password = args.password mfa_code = args.mfa_code +start_date = None +end_date = None +if args.start_date != "": + try: + start_date = datetime.datetime.strptime(args.start_date+"00:00:00.000000", "%Y-%m-%d%H:%M:%S.%f") + except ValueError: + print("Given start date " + args.start_date + " does not fit format YYYY-MM-DD") +if args.end_date != "": + try: + end_date = datetime.datetime.strptime(args.end_date+"23:59:59.999999", "%Y-%m-%d%H:%M:%S.%f") + except ValueError: + print("Given end date " + args.end_date + " does not fit format YYYY-MM-DD") load_dotenv(find_dotenv()) @@ -118,12 +135,9 @@ fields[i + (page * 100)]['execution_state'] = order['state'] if len(executions) > 0: - trade_count += 1 fields[i + (page * 100)]['execution_state'] = ("completed", "partially filled")[order['cumulative_quantity'] < order['quantity']] fields[i + (page * 100)]['first_execution_at'] = executions[0]['timestamp'] fields[i + (page * 100)]['settlement_date'] = executions[0]['settlement_date'] - elif order['state'] == "queued": - queued_count += 1 # paginate if orders['next'] is not None: page = page + 1 @@ -135,6 +149,39 @@ # print fields[i] # print "-------" + + +# CSV headers +keys = fields[0].keys() +keys = sorted(keys) +csv = ','.join(keys) + "\n" + +# CSV rows +for row in fields: + #Check if date is compliant with filters + date_object = datetime.datetime.strptime(fields[row]['created_at'], "%Y-%m-%dT%H:%M:%S.%fZ") + filtered = False + if start_date != None: + if start_date > date_object: + filtered = True + if end_date != None: + if end_date < date_object: + filtered = True + if not filtered: + if fields[row]['execution_state'] in ['completed', 'partially filled']: + trade_count += 1 + elif fields[row]['state'] == "queued": + queued_count += 1 + for idx, key in enumerate(keys): + if (idx > 0): + csv += "," + try: + csv += str(fields[row][key]) + except: + csv += "" + + csv += "\n" + # check we have trade data to export if trade_count > 0 or queued_count > 0: print("%d queued trade%s and %d executed trade%s found in your account." % @@ -145,22 +192,6 @@ print("No trade history found in your account.") quit() -# CSV headers -keys = fields[0].keys() -keys = sorted(keys) -csv = ','.join(keys) + "\n" - -# CSV rows -for row in fields: - for idx, key in enumerate(keys): - if (idx > 0): - csv += "," - try: - csv += str(fields[row][key]) - except: - csv += "" - - csv += "\n" # choose a filename to save to print("Choose a filename or press enter to save to `robinhood.csv`:") @@ -204,11 +235,6 @@ fields[i + (page * 100)][value] = dividend[value] fields[i + (page * 100)]['execution_state'] = order['state'] - - if dividend['state'] == "pending": - queued_dividends += 1 - elif dividend['state'] == "paid": - dividend_count += 1 # paginate if dividends['next'] is not None: page = page + 1 @@ -220,15 +246,7 @@ # print fields[i] # print "-------" - # check we have trade data to export - if dividend_count > 0 or queued_dividends > 0: - print("%d queued dividend%s and %d executed dividend%s found in your account." % - (queued_dividends, "s" [queued_count == 1:], dividend_count, - "s" [trade_count == 1:])) - # print str(queued_count) + " queded trade(s) and " + str(trade_count) + " executed trade(s) found in your account." - else: - print("No dividend history found in your account.") - quit() + # CSV headers keys = fields[0].keys() @@ -237,15 +255,39 @@ # CSV rows for row in fields: - for idx, key in enumerate(keys): - if (idx > 0): - csv += "," - try: - csv += str(fields[row][key]) - except: - csv += "" + #Check if date is compliant with filters + date_object = datetime.datetime.strptime(fields[row]['payable_date']+"12", "%Y-%m-%d%H") + filtered = False + if start_date != None: + if start_date > date_object: + filtered = True + if end_date != None: + if end_date < date_object: + filtered = True + if not filtered: + if fields[row]['state'] == "paid": + dividend_count += 1 + elif fields[row]['state'] == "pending": + queued_dividends += 1 + for idx, key in enumerate(keys): + if (idx > 0): + csv += "," + try: + csv += str(fields[row][key]) + except: + csv += "" - csv += "\n" + csv += "\n" + + # check we have trade data to export + if dividend_count > 0 or queued_dividends > 0: + print("%d queued dividend%s and %d executed dividend%s found in your account." % + (queued_dividends, "s" [queued_count == 1:], dividend_count, + "s" [trade_count == 1:])) + # print str(queued_count) + " queded trade(s) and " + str(trade_count) + " executed trade(s) found in your account." + else: + print("No dividend history found in your account.") + quit() # choose a filename to save to print("Choose a filename or press enter to save to `dividends.csv`:") From be6a3a54a822fe060ea73e1dad0f3e5b38a78024 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 22 Aug 2019 12:43:04 -0400 Subject: [PATCH 6/6] Fix for count loaded as float --- profit_extractor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/profit_extractor.py b/profit_extractor.py index 90e5a00..39cce91 100644 --- a/profit_extractor.py +++ b/profit_extractor.py @@ -99,6 +99,7 @@ def profit_extractor(csv_val, filename): ws_count_temp = ws_count for q, p in buy_list: total_buy += float(q)*float(p) + q = int(q) for i in range(0, q): if ws_count_temp > 0: amount = p-float(row.average_price)