From 29c3cfe2cf716d0901534795a2f1bd2108a8d7b9 Mon Sep 17 00:00:00 2001 From: Ivan Seredkin Date: Tue, 7 Jan 2025 09:21:51 -0500 Subject: [PATCH 1/2] Adjust analyst ratings field names --- README.md | 22 +++++++++++----------- robinhood.py | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d918fbe..e86140b 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The bot's analytical system incorporates moving averages and Robinhood analyst r This is Robinhood's analyst rating system example: -![Robinhood Analyst Ratings](images/robinhood_analyst_ratings.png) +![Robinhood Analyst Ratings](images/analyst_ratings.png) #### AI-Powered Decision-Making System @@ -94,9 +94,9 @@ Analyze the provided portfolio and watchlist data to recommend: "average_buy_price": 226.93, "50_day_mavg_price": 226.88, "200_day_mavg_price": 202.76, - "robinhood_analyst_sell_opinion": "Regulators have a keen eye on Apple, and recent regulations have chipped away at parts of Apple\u2019s sticky ecosystem. ", - "robinhood_analyst_buy_opinion": "Apple has a stellar balance sheet and sends great amounts of cash flow back to shareholders.", - "robinhood_analyst_summary_distribution": "sell: 6%, buy: 67%, hold: 27%" + "analyst_sell_opinion": "Regulators have a keen eye on Apple, and recent regulations have chipped away at parts of Apple\u2019s sticky ecosystem. ", + "analyst_buy_opinion": "Apple has a stellar balance sheet and sends great amounts of cash flow back to shareholders.", + "analyst_summary_distribution": "sell: 6%, buy: 67%, hold: 27%" }, "NVDA": { "price": 147.13, @@ -104,9 +104,9 @@ Analyze the provided portfolio and watchlist data to recommend: "average_buy_price": 147.19, "50_day_mavg_price": 126.67, "200_day_mavg_price": 106.37, - "robinhood_analyst_sell_opinion": "Nvidia\u2019s gaming GPU business has often seen boom-or-bust cycles based on PC demand and, more recently, cryptocurrency mining.", - "robinhood_analyst_buy_opinion": "The firm has a first-mover advantage in the autonomous driving market that could lead to widespread adoption of its Drive PX self-driving platform.", - "robinhood_analyst_summary_distribution": "sell: 0%, buy: 92%, hold: 8%" + "analyst_sell_opinion": "Nvidia\u2019s gaming GPU business has often seen boom-or-bust cycles based on PC demand and, more recently, cryptocurrency mining.", + "analyst_buy_opinion": "The firm has a first-mover advantage in the autonomous driving market that could lead to widespread adoption of its Drive PX self-driving platform.", + "analyst_summary_distribution": "sell: 0%, buy: 92%, hold: 8%" }, ... } @@ -119,15 +119,15 @@ Analyze the provided portfolio and watchlist data to recommend: "price": 53.05, "50_day_mavg_price": 52.99, "200_day_mavg_price": 45.76, - "robinhood_analyst_sell_opinion": "The possibility of highly competitive foreign internet service providers reentering China over the next 10-20 years.", - "robinhood_analyst_buy_opinion": "Compliance costs can rise to a point where they become significant barriers to entry to the Chinese internet industry.", - "robinhood_analyst_summary_distribution": "sell: 3%, buy: 95%, hold: 2%" + "analyst_sell_opinion": "The possibility of highly competitive foreign internet service providers reentering China over the next 10-20 years.", + "analyst_buy_opinion": "Compliance costs can rise to a point where they become significant barriers to entry to the Chinese internet industry.", + "analyst_summary_distribution": "sell: 3%, buy: 95%, hold: 2%" }, "NSSC": { "price": 39.1, "50_day_mavg_price": 39.5, "200_day_mavg_price": 44.88, - "robinhood_analyst_summary_distribution": "sell: 0%, buy: 83%, hold: 17%" + "analyst_summary_distribution": "sell: 0%, buy: 83%, hold: 17%" }, ... } diff --git a/robinhood.py b/robinhood.py index f7d7f6b..4f6b977 100644 --- a/robinhood.py +++ b/robinhood.py @@ -103,9 +103,9 @@ def enrich_with_analyst_ratings(stock_data, symbol): last_sell_rating = next((rating for rating in ratings['ratings'] if rating['type'] == "sell"), None) last_buy_rating = next((rating for rating in ratings['ratings'] if rating['type'] == "buy"), None) if last_sell_rating: - stock_data["robinhood_analyst_sell_opinion"] = last_sell_rating['text'].decode('utf-8') + stock_data["analyst_sell_opinion"] = last_sell_rating['text'].decode('utf-8') if last_buy_rating: - stock_data["robinhood_analyst_buy_opinion"] = last_buy_rating['text'].decode('utf-8') + stock_data["analyst_buy_opinion"] = last_buy_rating['text'].decode('utf-8') if 'summary' in ratings and ratings['summary']: summary = ratings['summary'] total_ratings = sum([summary['num_buy_ratings'], summary['num_hold_ratings'], summary['num_sell_ratings']]) @@ -113,7 +113,7 @@ def enrich_with_analyst_ratings(stock_data, symbol): buy_percent = summary['num_buy_ratings'] / total_ratings * 100 sell_percent = summary['num_sell_ratings'] / total_ratings * 100 hold_percent = summary['num_hold_ratings'] / total_ratings * 100 - stock_data["robinhood_analyst_summary_distribution"] = f"sell: {sell_percent:.0f}%, buy: {buy_percent:.0f}%, hold: {hold_percent:.0f}%" + stock_data["analyst_summary_distribution"] = f"sell: {sell_percent:.0f}%, buy: {buy_percent:.0f}%, hold: {hold_percent:.0f}%" return stock_data From 78a5258f7cdd67aae4989d1cc4759a1787c10c0c Mon Sep 17 00:00:00 2001 From: Ivan Seredkin Date: Tue, 7 Jan 2025 11:55:16 -0500 Subject: [PATCH 2/2] #38 Add support for MFA for Robinhood login --- README.md | 13 +++++++++++++ config.py.sample | 1 + robinhood.py | 22 ++++++++++++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e86140b..766b231 100644 --- a/README.md +++ b/README.md @@ -291,6 +291,7 @@ Configuration parameters: OPENAI_API_KEY = "..." # OpenAI API key ROBINHOOD_USERNAME = "..." # Robinhood username ROBINHOOD_PASSWORD = "..." # Robinhood password +ROBINHOOD_MFA_SECRET = "" # Robinhood MFA secret (if enabled) # Basic config parameters MODE = "demo" # Trading mode (demo, auto, manual) @@ -313,6 +314,18 @@ OPENAI_MODEL_NAME = "gpt-4o-mini" # OpenAI model name MAX_POST_DECISIONS_ADJUSTMENTS = False # Maximum number of adjustments to make (False - disable adjustments) ``` + +#### Robinhood MFA Secret +If you have Multi-Factor Authentication (MFA) enabled on your Robinhood account, you will need to provide the `ROBINHOOD_MFA_SECRET`. +Here is how you can get it: +1. Log in to your Robinhood account on your phone. Important to use your phone because it will display the secret key but not the QR code. +2. Navigate to the security settings. +3. Enable MFA if it is not already enabled. When setting up MFA, you will be asked to select an authentication method on your phone. Choose "Authenticator app" and Robinhood will provide you with a secret key. This is your `ROBINHOOD_MFA_SECRET`. +4. Copy this secret key and paste it into the `config.py` file. +5. Enter the same secret key into your authentication app on the same PC where you run the script (e.g., Google Authenticator). Note: If you enter the secret on a different device, it will generate a different value. +6. After entering the same secret on the same PC, use the generated TOTP number to authenticate with the Robinhood app. + + ### Run To start the bot, run the following command in your terminal: ```sh diff --git a/config.py.sample b/config.py.sample index a8759f5..f525d30 100644 --- a/config.py.sample +++ b/config.py.sample @@ -2,6 +2,7 @@ OPENAI_API_KEY = "..." # OpenAI API key ROBINHOOD_USERNAME = "..." # Robinhood username ROBINHOOD_PASSWORD = "..." # Robinhood password +ROBINHOOD_MFA_SECRET = "" # Robinhood MFA secret (if enabled) # Basic config parameters MODE = "demo" # Trading mode (demo, auto, manual) diff --git a/robinhood.py b/robinhood.py index 4f6b977..5792d42 100644 --- a/robinhood.py +++ b/robinhood.py @@ -4,9 +4,27 @@ import pandas as pd from log import * -from config import MODE, ROBINHOOD_USERNAME, ROBINHOOD_PASSWORD +import pyotp +import sys +from config import MODE, ROBINHOOD_USERNAME, ROBINHOOD_PASSWORD, ROBINHOOD_MFA_SECRET + + +# Attempt to login to Robinhood +try: + if ROBINHOOD_MFA_SECRET: + log_debug("Attempting to login to Robinhood with MFA...") + mfa_code = pyotp.TOTP(ROBINHOOD_MFA_SECRET).now() + log_debug(f"Generated MFA code: {mfa_code}") + login = rh.login(ROBINHOOD_USERNAME, ROBINHOOD_PASSWORD, mfa_code=mfa_code) + log_debug("Robinhood login successful with MFA.") + else: + log_debug("Attempting to login to Robinhood without MFA...") + login = rh.login(ROBINHOOD_USERNAME, ROBINHOOD_PASSWORD) + log_debug("Robinhood login successful without MFA.") +except Exception as e: + log_error(f"An error occurred during Robinhood login: {e}") + sys.exit(1) -rh.login(ROBINHOOD_USERNAME, ROBINHOOD_PASSWORD) # Run a Robinhood function with retries and delay between attempts (to handle rate limits) def rh_run_with_retries(func, *args, max_retries=3, delay=60, **kwargs):