jmfernandes / robin_stocks

This is a library to use with Robinhood Financial App. It currently supports trading crypto-currencies, options, and stocks. In addition, it can be used to get real time ticker information, assess the performance of your portfolio, and can also get tax documents, total dividends paid, and more. More info at
http://www.robin-stocks.com
MIT License
1.72k stars 465 forks source link

401 Client Error: Unauthorized for url. #464

Closed SingularityMan closed 8 months ago

SingularityMan commented 8 months ago

I get authentication errors after running my script for about 24 hours. Initially when the script starts I log in:

# Login to Robinhood
login = r.login(username, password)
token_expires_at = time.time()+login['expires_in']
def refresh_token(token_expires_at):
    global username
    global password

    # Check if the token is expired
    if time.time() > token_expires_at:
        # If the token is expired, refresh it
        print("Token expired. Refreshing token.")
        r.logout()
        login = r.login(username, password)
        token_expires_at = time.time()+login['expires_in']
    else:
        print("Token is still valid. Expires in", token_expires_at-time.time(), "seconds.")
    return token_expires_at

And in the while loop it is supposed to monitor for token expiration prior to making any decisions:

while True:

     time.sleep(1)

     token_expires_at = refresh_token(token_expires_at)
Token is still valid.
Waiting 60 seconds before making transactions.
401 Client Error: Unauthorized for url: https://api.robinhood.com/positions/?nonzero=true
401 Client Error: Unauthorized for url: https://api.robinhood.com/portfolios/
401 Client Error: Unauthorized for url: https://api.robinhood.com/accounts/
401 Client Error: Unauthorized for url: https://phoenix.robinhood.com/accounts/unified
Traceback (most recent call last):
  File "C:\Users\user\PycharmProjects\npc_game\test_game\stock_bot.py", line 265, in <module>
    buying_power = float(r.account.load_phoenix_account(info='account_buying_power')['amount'])
TypeError: 'NoneType' object is not subscriptable

Which forces me to restart the script altogether to be able to trade again. This is far from ideal. I want to be able to authenticate this indefinitely. I thought the login function would handle that since it returns an authentication token expiration time. Why is it not re-authenticating until I have to restart the script all over again?

UPDATE: Actually, I just noticed the token is still showing as valid for some reason. This leads me to believe two things:

1 - The token is refreshing when its supposed to when I'm not looking at the console, and the console's output doesn't go that far back.

2 - The script isn't detecting when it is expired but I did a quick test by setting token_expires_at to -1 and it did run the portion of the script.

So in this case there is more to it than that. Is it possible to refresh the authentication token after you initially run the script?

UPDATE: May be solved. I set it to expire every one hour. So we'll see if it successfully refreshes a new token.

Adelantado commented 8 months ago

Have to say that 401 Client Error , aka, you've been kicked out of your session is a common problem I face, but no to your extent ( 24 hours ) ,and I will bet everybody does. You need to accept that no matter what you do or how you code, it is gonna happen, sooner or later. with that said..... and no offense ... IMO , checking if session is active before you execute any code is a waste of time. You will get a 401 error as soon as you make any meaningful request to their servers, so no need to check anything. My suggestion ( and I did it ) would be to modify your entire code in an effort to overcome the inevitable: Wrap all you code in a try except statement. Catch the exception, figure if is a 401 error, and if so .... loop back to a point in your code where you just can delete the .token folder and relog in..... ( you can even skip all the log out nonsense since you were kicked out already ) .

To be honest is a bit more complicate than that, but I am confident you are smart guy, you get the idea, and can figure how to make it work within your code .... after all U got this far right ?? Hope it helps and don't give up !!.

SingularityMan commented 8 months ago

@Adelantado I'm sure I'll find a way. I modified it further to refresh every 12 hours. From the testing I did in the 1-hour refresh, it does seem to be refreshing a new token because it prints out a new token every time it does so MAYBE it works, but I will have to wait 2 days to know for sure. Fingers crossed, but I'm not holding my breath.

Adelantado commented 8 months ago

I must admit I do not understand the reason nor the need for what you call refresh. I run my BOT 24/7 , sometimes for 5 / 6 days strait before getting a 401 error, and when I do, the logic I did describe earlier takes care of it; no need to manually restart anything, which is, for what I am understanding, what you are trying to fix/figure. I can get kicked out at 3 am and be up and running by 3:02 like nothing happened.

SingularityMan commented 8 months ago

Well from my understanding you need an authentication token to run robin_stocks and I need it running indefinitely.

But its true what you said. The 75 tickers in my portfolio are evaluated for trading at 9:30am and the trades should finish by 11:30am. I use a local LLM (mistral-7B-instruct) to analyze a ticker's news, earnings, fundamentals and price in order to make a decision on whether to buy, sell or hold automatically, the decision which would prompt robin_stocks to automatically make the purchases by an equal amount (buying_power as of today / len(ticker_list)) or sell all the shares of a ticker if it decides to sell.

But you can't really reset an authentication token until it expires, from my understanding. I think it might have to do with the fact that it is stored in a pickle file but I don't really wanna mess with that so I set the expiration to 12 hours outside of trading hours before refreshing a new token. Here's hoping it actually works tomorrow this way.

But the point I'm trying to get at is that once the evaluation-to-trade process starts, I need to be authenticated throughout the whole process in that two-hour window otherwise the trades might not go through entirely, if at all.

ssmanji89 commented 8 months ago

could function the login and pass/call it at runtime; too inefficient?

image

Adelantado commented 8 months ago

Well from my understanding you need an authentication token to run robin_stocks and I need it running indefinitely.

But its true what you said. The 75 tickers in my portfolio are evaluated for trading at 9:30am and the trades should finish by 11:30am. I use a local LLM (mistral-7B-instruct) to analyze a ticker's news, earnings, fundamentals and price in order to make a decision on whether to buy, sell or hold automatically, the decision which would prompt robin_stocks to automatically make the purchases by an equal amount (buying_power as of today / len(ticker_list)) or sell all the shares of a ticker if it decides to sell.

But you can't really reset an authentication token until it expires, from my understanding. I think it might have to do with the fact that it is stored in a pickle file but I don't really wanna mess with that so I set the expiration to 12 hours outside of trading hours before refreshing a new token. Here's hoping it actually works tomorrow this way.

But the point I'm trying to get at is that once the evaluation-to-trade process starts, I need to be authenticated throughout the whole process in that two-hour window otherwise the trades might not go through entirely, if at all.

You will have to forgive me if I am just not simply understanding your need to reset anything. As far as I know ( based on how I code ) I find no need to reset the authentication token ever. I mean, I get for A or B kicked out of the session here and there, but then I just pick up the fact thru the exception, and just relog in automatically and move on.

IMO the fact that you "refresh your token at 9:30 every day" provides no assurance whatsoever that your routine will run for two hours strait as needed; session still could be terminated for A or B at any given time having nothing to do with your code or how long ago was the last time you logged on. Thought you were looking for ideas/suggestions as how to keep your bot running indefinitely while trying to avoid to restarted manually due the 401 error which occurs at Robins ends. Sorry if I misunderstood.

SingularityMan commented 8 months ago

could function the login and pass/call it at runtime; too inefficient?

image

I don't think that's really going to solve my problem. The issue is not logging in and authenticating, the issue is refreshing the authentication token. The code you provided seems to be logging in with extra steps and I'm not sure how a function calling login would help me get past this issue.

SingularityMan commented 8 months ago

UPDATE: The token seems to have refreshed successfully after setting it to 12 hours instead of 24 so it should be good.