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.69k stars 459 forks source link

API to trade options #7

Closed briansp2020 closed 5 years ago

briansp2020 commented 5 years ago

I'd like to have ability to buy/sell options using this API. You mentioned that you are working on such capability https://github.com/jmfernandes/robin_stocks/issues/3. Are you close to getting it working? I'd like to help if you can give me pointers. Though I'm new to REST API and Robinhood API, I'm a software engineer so I think I can help if you want.

Also, thanks for great software!

jmfernandes commented 5 years ago

Unfortunately I haven't been able to make any progress on that. If you want to try to figure out how to buy and sell options, you can use the function in the helper.py file called 'request_post()'. It takes in a url and a payload. The documentation tells more about what the format looks like. But as long as you use the correct url and payload then the API can execute the command.

I'm pretty sure the url is https://api.robinhood.com/options/orders/ but I have no idea what the payload needs to be. I even used the developer tools on chrome to see what calls to the API were being made when placing orders for options on the robin hood website. As far as I can tell, I was inputing the same information as the website but it always failed.

briansp2020 commented 5 years ago

Thank you for that information. Are you familiar with JavaScript? I found this code (https://github.com/Ladinn/algotrader/blob/master/objects/broker/robinhood/OptionOrder.js) which I think is code to build payload information for option order placement. I'll experiment a bit ans see if I can figure anything out. Also, if you can give me any code you use to experiment, that would be helpful.

Thanks!

jmfernandes commented 5 years ago

That's awesome. Thanks for the link. I'm still getting a 500 interval server error. It looks like this is exactly what is in the Java. So idk if his code is wrong or mine. Here is a function I wrote.

def order_buy_option(symbol,quantity,expirationDate,strike,optionType,timeInForce='gtc'):

try:
    symbol = symbol.upper().strip()
except AttributeError as message:
    print(message)
    return None

optionID = helper.id_for_option(symbol,expirationDate,strike,optionType)

payload = {
'account': profiles.load_account_profile(info='url'),
'direction': 'debit',
'time_in_force': timeInForce,
'legs': {'position_effect': 'open', 'side' : 'buy', 'ratio_quantity': 1, 'option': urls.option_instruments(optionID) },
'type': 'market',
'quantity': quantity
}

url = urls.option_orders()
data = helper.request_post(url,payload)

return(data)
briansp2020 commented 5 years ago

Hi Joshua, I found someone who claims that he was able to place option order using the algotrader software. (see https://github.com/Ladinn/algotrader/issues/12) It seems that the had to send additional parameters on top of what you provided as well as passing 'ratio_quantity' value as string. However, I'm not sure what the ref_id value he is using is. Do you have any idea?

jmfernandes commented 5 years ago

I'm not sure either. There is a chain id associated with options. maybe its that? Also I notice that they have the type be 'limit' instead of 'market'. Maybe that is an error as well? I don't think I have time this weekend to do any testing. Could you help me out with that?

in helper there is a function, id_for_chain and id_for_group. Use those to get ids and set one as the ref_id. If it doesn't work then try the other id. Also try setting the type to 'limit'. I don't think passing in ratio_quantity as an int instead of string would generate an error....I could be wrong though.

briansp2020 commented 5 years ago

I tried

def order_buy_option_limit(symbol,quantity,expirationDate,strike,optionType,price,timeInForce='gfd'):

try:
    symbol = symbol.upper().strip()
except AttributeError as message:
    print(message)
    return None

optionID = helper.id_for_option(symbol,expirationDate,strike,optionType)

payload = {
'account': profiles.load_account_profile(info='url'),
'direction': 'debit',
'time_in_force': timeInForce,
'legs': {'position_effect': 'open', 'side' : 'buy', 'ratio_quantity': '1', 'option': urls.option_instruments(optionID) },
'type': 'limit',
'price': float(price),
'trigger': 'immediate',
'override_day_trade_checks':False,
'override_dtbp_checks':False,     
'quantity': quantity
}

print (payload)
url = urls.option_orders()
data = helper.request_post(url,payload)

return(data)

which sends

{'account': '...', 'direction': 'debit', 'time_in_force': 'gfd', 'legs': {'position_effect': 'open', 'side': 'buy', 'ratio_quantity': '1', 'option': 'https://api.robinhood.com/options/instruments/c6a86cbc-5373-4fb2-99b4-8a91bfae1ee4/'}, 'type': 'limit', 'price': 0.23, 'trigger': 'immediate', 'override_day_trade_checks': False, 'override_dtbp_checks': False, 'quantity': 1}

as payload but I still get error. I'll try using the id you mentioned.

Thanks

briansp2020 commented 5 years ago

I tried

payload = {
'account': profiles.load_account_profile(info='url'),
'direction': 'debit',
'legs': {'position_effect': 'open', 'side' : 'buy', 'ratio_quantity': 1, 'option': urls.option_instruments(optionID) },
'price': float(price),
'time_in_force': timeInForce,
'trigger': 'immediate',
'type': 'limit',
'quantity': quantity,
'override_day_trade_checks': False,
'override_dtbp_checks': False,
'ref_id':str(uuid.uuid4())
}

and now I get 400 error. When I provided wrong value for an option order using algotrader, I was getting 400 error as well. So, I'm guessing the name of the fields are right but some of the values are wrong. Unfortunately, I can't get exact error messages from request_post. See https://github.com/Ladinn/algotrader/issues/12#issuecomment-457749773

jmfernandes commented 5 years ago

yeah I tried

payload = {
"quantity":"1",
"direction":"debit",
"type":"limit",
"account":r.profiles.load_account_profile(info="url"),
"time_in_force":"gfd",
"trigger":"immediate",
"legs":[{"side":"buy","option":r.urls.option_instruments(optionID),"position_effect":"open","ratio_quantity":"1"}],
"override_day_trade_checks":"false",
"override_dtbp_checks":"false"}

and I'm getting the 500 error. and sometimes a 400 error as i play around with parameters. Can't find what I'm doing differently than the javascript code you linked.

jmfernandes commented 5 years ago

I may have figured it out. I found a comment that the session info needs to be updated to

'Content-type': 'application/json; charset=utf-8',

in my code its

"Content-Type": "application/x-www-form-urlencoded; charset=utf-8",

but then i think it needs to be set back to original after setting order for option?

EDIT: Nope. not working.

m256 commented 5 years ago

Hi @jmfernandes , @briansp2020 , Have any of you figured this out yet? Cheers

briansp2020 commented 5 years ago

@m256 I used fast_arrow to make option trade. See https://github.com/westonplatter/fast_arrow for more info. Hope that helps.

m256 commented 5 years ago

@m256 I used fast_arrow to make option trade. See https://github.com/westonplatter/fast_arrow for more info. Hope that helps.

Thanks for sharing that @briansp2020 . I am still curious if you were able to figure out the payload details. I'm experimenting with a version in MATLAB and am using the exact same payload at @jmfernandes in https://github.com/jmfernandes/robin_stocks/issues/7#issuecomment-458419226 with an additional ref_id parameter. However, the order does not go through and I get a response with all existing orders. My payload looks like this: 'account', 'https://api.robinhood.com/accounts/',myAccountNumber,'/',... 'direction', 'debit',... 'legs', {"side":"buy","option":"https://api.robinhood.com/options/instruments/24881756-c18a-48ee-8251-37db1c538346/","position_effect":"open","ratio_quantity":"1"},... 'price', '0.11',... 'quantity', '1.0',... 'time_in_force', 'gfd',... 'trigger', 'immediate',... 'type', 'limit',... 'override_day_trade_checks', 'false',... 'override_dtbp_checks', 'false',... 'ref_id', char(java.util.UUID.randomUUID),...

The payload of option_order.py from https://github.com/westonplatter/fast_arrow also looks the same: payload = json.dumps({ "account": client.account_url, "direction": direction, "legs": legs, "price": price, "quantity": quantity, "time_in_force": time_in_force, "trigger": trigger, "type": order_type, "override_day_trade_checks": False, "override_dtbp_checks": False, "ref_id": str(uuid.uuid4()) }) Just not able to figure out why the option orders are not going through and I'm getting a response with all my past option orders.

Jmarkaba commented 5 years ago

I think I got it working for an option limit buy based on your code @jmfernandes . May not be the cleanest possible solution because I had to change the request_post more than i would like, but it works and can be refactored: https://github.com/jmfernandes/robin_stocks/pull/35.

I tested using print(robin.order_buy_option_limit('0.01', 'FIT', 1, '2019-08-23', '3', 'call')) If you and @briansp2020 could test my changes and verify it works, that'd be gr8.

jmfernandes commented 5 years ago

@Jmarkaba yeah its unfortunate that you had to change request_post, but its fine. I need to refactor that function anyway, to get rid of the logic for logging in. All of that should be in the log in function. request_post should only post and be as "dumb" as possible. But I'll worry about that later.

I have a question, could you change the "direction" to "credit" and the "side" inside "legs" to "sell" and see if you can do a sell limit? And if so write up that function as well? Thanks a lot for help as always!

and yes I'll test the changes on my end

Jmarkaba commented 5 years ago

Yeah I'll work on abstracting the function and making it work for all cases. Also the request post should just return a status code, data tuple I think. Or maybe just data. I'll see what I can work out

jmfernandes commented 5 years ago

What parameters did you use for stock, strike, etc.? I tried a few different options and I was getting an error, but I'll try again tomorrow during open trading hours

Edit. Nvm. I saw your post saying what you used.

jmfernandes commented 5 years ago

Got it working for me, so i added documentation, added a function to sell options (short puts and short calls), added a function to cancel option orders, and merged it into master.

jmfernandes commented 5 years ago

Buying and selling options is now supported.

dhoscovi87 commented 1 year ago

Hi Fernandes for the option selling or buying I need to have updated all others functions inside that function?