torreyleonard / algotrader

Simple algorithmic stock and option trading for Node.js.
https://www.npmjs.com/package/algotrader
Apache License 2.0
637 stars 120 forks source link

Robinhood OptionOrder: Server Error 500 #13

Closed torreyleonard closed 5 years ago

torreyleonard commented 5 years ago

Robinhood is responding with status code 500 (server error) when submitting an option order.

@briansp2020:

Thanks for your help. I tried to place an option order but was not successful. I'm new to javascript so I'm sure I did something wrong. Could you take a look at the code below and let me know what I'm doing wrong?

myUser.authenticate()
.then(() => {
// User was authenticated

// First, we'll get the instrument and tradable expiration dates for SPY.
Instrument.getBySymbol("SPY").then(ins => {
    OptionInstrument.getExpirations(myUser, ins).then(option => {
        // Next, we'll fetch an option chain for the upcoming expiration date.
        OptionInstrument.getChain(myUser, ins, option[0], "put").then(optionChain => {
                console.log(optionChain[4]);

            // An array of OptionInstruments will be returned. See the example below.
        // You'll then want to find the specific OptionInstrument that you want to trade.
        // In this example, we'll buy using the first element in the array.
        let order = new OptionOrder(null, myUser, optionChain[4], "credit", "gtc", "buy", "market", 1);
        order.submit().then(res => {
                // Order was submitted, the response will be parsed as a completed OptionOrder.
        console.log("Order submitted");
        console.log(res);
        });
        })
    })
    });
})
.catch(error => {
    // Either the request failed, or Robinhood responded with an error.
    // (Ex: you don't have internet access or your user credentials were incorrect)
})

I get the following error message

OptionInstrument {
  url: 'https://api.robinhood.com',
  tradability: 'tradable',
  strikePrice: 260,
  state: 'active',
  type: 'put',
  symbol: 'SPY',
  minTicks: { cutoff_price: '0.00', below_tick: '0.01', above_tick: '0.01' },
  instrumentURL: 'https://api.robinhood.com/options/instruments/487f72f2-b797-4016-8ff7-8dd172029808/',
  ids: 
   { chain: 'c277b118-58d9-4060-8dc5-a3b5898955cb',
     option: '487f72f2-b797-4016-8ff7-8dd172029808' },
  dates: 
   { expiration: 2019-03-04T00:00:00.000Z,
     created: 2019-01-25T03:08:09.376Z,
     updated: 2019-01-25T03:08:09.376Z } }
(node:25477) UnhandledPromiseRejectionWarning: Error: Robinhood responded with code 500 | <h1>Server Error (500)</h1>

> Please report all unexpected errors on GitHub: https://git.io/vpYYL

    at Function.handleResponse (/home/briansp/node_modules/algotrader/objects/broker/robinhood/Robinhood.js:23:11)
    at Request.request.post [as _callback] (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:98:22)
    at Request.self.callback (/home/briansp/node_modules/request/request.js:185:22)
    at emitTwo (events.js:126:13)
    at Request.emit (events.js:214:7)
    at Request.<anonymous> (/home/briansp/node_modules/request/request.js:1161:10)
    at emitOne (events.js:116:13)
    at Request.emit (events.js:211:7)
    at IncomingMessage.<anonymous> (/home/briansp/node_modules/request/request.js:1083:12)
    at Object.onceWrapper (events.js:313:30)
(node:25477) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:25477) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Server error 500 is the same error message I'm getting from the python api. If I change "gtc" to "GTC" as shown in the example in readme, I get 400 error along with the following error message.

(node:25697) UnhandledPromiseRejectionWarning: Error: Robinhood responded with code 400.

{
  "time_in_force": [
    "\"GTC\" is not a valid choice."
  ]
}

Any ideas? I'd like to get the option order placement working using this API before trying to get the python API working. Any help would be appreciated.

Thanks!

Originally posted by @briansp2020 in https://github.com/Ladinn/algotrader/issues/12#issuecomment-457749773

torreyleonard commented 5 years ago

Fixed in version 2.2.0.

@briansp2020 take a look at OptionOrder, the requests are verified as working. Hopefully it helps!

briansp2020 commented 5 years ago

@Ladinn Thank you for looking into this. I think I updated properly to 2.2.0. Now I have different behavior.

[ 2019-01-30T00:00:00.000Z,
  2019-02-01T00:00:00.000Z,
  2019-02-04T00:00:00.000Z,
  2019-02-06T00:00:00.000Z,
  2019-02-08T00:00:00.000Z,
  2019-02-11T00:00:00.000Z,
  2019-02-13T00:00:00.000Z,
  2019-02-15T00:00:00.000Z,
  2019-02-19T00:00:00.000Z,
  2019-02-20T00:00:00.000Z,
  2019-02-22T00:00:00.000Z,
  2019-02-25T00:00:00.000Z,
  2019-02-27T00:00:00.000Z,
  2019-03-01T00:00:00.000Z,
  2019-03-04T00:00:00.000Z,
  2019-03-08T00:00:00.000Z,
  2019-03-15T00:00:00.000Z,
  2019-03-29T00:00:00.000Z,
  2019-04-18T00:00:00.000Z,
  2019-05-17T00:00:00.000Z,
  2019-06-21T00:00:00.000Z,
  2019-06-28T00:00:00.000Z,
  2019-09-20T00:00:00.000Z,
  2019-09-30T00:00:00.000Z,
  2019-12-20T00:00:00.000Z,
  2019-12-31T00:00:00.000Z,
  2020-01-17T00:00:00.000Z,
  2020-03-20T00:00:00.000Z,
  2020-06-19T00:00:00.000Z,
  2020-09-18T00:00:00.000Z,
  2020-12-18T00:00:00.000Z,
  2021-01-15T00:00:00.000Z,
  2021-12-17T00:00:00.000Z ]
[]
(node:6388) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'isAuthenticated'     of null
    at _validate     (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:57:23)
    at new OptionOrder     (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:29:4)
    at OptionInstrument.getChain.then.optionChain (/home/briansp/git/jstrade/test.js:30:19)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:6388) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated     either by throwing inside of an async function without a catch block, or by rejecting a promise which was     not handled with .catch(). (rejection id: 1)
(node:6388) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the     future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit     code.

Expiration date returned has time at the end. It used to return only the date. getChange does not return any chain information even when I manually pass the date in the old format (ex. '2019-01-30').

torreyleonard commented 5 years ago

@briansp2020 Check out the new tutorial in the README. It'll outline how to properly send an option order in the latest update.

jmfernandes commented 5 years ago

Out of curiosity, how did you figure out what the new payload should be? I tried using chrome dev tools to look at what information was being sent by the website, but I didn't get anywhere.

briansp2020 commented 5 years ago

@Ladinn I'm new to JS. So, I'm having a bit of trouble figuring out what to do. I started from your example in the README again and it still does not work properly. Also, I thought the code I used was identical to the example...

Anyway, below is my new attempt to get the option order working written based on your latest README. The following code does not get the expiration date where as my previous code at least gets the expiration date. I can't figure out what the difference is between the two though...

async function gains(user) {

  // First, we'll get the instrument that corresponds to the symbol TLRY.
  const tlry = await Instrument.getBySymbol('TLRY');
  const expirations = await OptionInstrument.getExpirations(user, tlry);

  // Next, we'll fetch an option chain containing puts for the upcoming expiration date.
  // This will return an array of OptionInstruments. See the example in the next section below.
  const chain = await OptionInstrument.getChain(user, tlry, expirations[0], 'put');

  // Now that we have the option chain, we'll need to find which OptionInstrument to trade
  // based on its strike price and expiration date. See the example below for how to sort them.
  // For now, we'll just take the first option contract in the array.
  const optionToBuy = chain[0];
  console.log(optionToBuy)

  // Finally, we can create and place an order like so:
  let order = new OptionOrder(user, {
    side: 'buy',
    type: 'limit', // Note: Robinhood does not allow market buy orders
    price: 1,
    timeInForce: 'gtc',
    quantity: 1,
    option: optionToBuy
  });

  console.log(order)
  //order.submit().then(executedOrder => {
    // Success!
  //  console.log(executedOrder);
  //}).catch(error => console.error(error));

}

myUser.authenticate()
    .then(() => {
        // User was authenticated
        console.log("Authenticated!")

        gains(myUser);
    })
    .catch(error => {
    // Either the request failed, or Robinhood responded with an error.
    // (Ex: you don't have internet access or your user credentials were incorrect)
    })

$ node --inspect test.js 
Debugger listening on ws://127.0.0.1:9229/e411b881-72fb-40bf-bc48-cd9075d4b7be
For help, see: https://nodejs.org/en/docs/inspector
Authenticated!
undefined
(node:22155) UnhandledPromiseRejectionWarning: Error: Object property 'optionInstrument' must be an instance of the OptionInstrument class
    at _validate     (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:66:54)
    at new OptionOrder (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:29:4)
    at gains (/home/briansp/git/jstrade/test.js:33:15)
    at processTicksAndRejections (internal/process/next_tick.js:81:5)
(node:22155) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:22155) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Any help would be appreciated.

Thanks!

briansp2020 commented 5 years ago

Actually, it is getting the expiration date. It's failing to get the chain which is the same problem I was getting with my original code.

$ node --inspect test.js 
Debugger listening on ws://127.0.0.1:9229/aa535304-e569-4a46-bc54-fd55ccde3f07
For help, see: https://nodejs.org/en/docs/inspector
Authenticated!
Instrument {
  url: 'https://api.robinhood.com',
  name: 'Tilray, Inc. Class 2 Common Stock',
  simpleName: 'Tilray',
  symbol: 'TLRY',
  listDate: 2018-07-19T00:00:00.000Z,
  country: 'CA',
  tradeable: true,
  type: 'stock',
  bloomberg: 'EQ0000000065883904',
  state: 'active',
  id: '1ca933b1-ec38-45f3-b815-7d2189103133',
  urls:
   { market: 'https://api.robinhood.com/markets/XNAS/',
     fundamentals: 'https://api.robinhood.com/fundamentals/TLRY/',
     quote: 'https://api.robinhood.com/quotes/TLRY/',
     instrument:
      'https://api.robinhood.com/instruments/1ca933b1-ec38-45f3-b815-7d2189103133/',
     splits:
      'https://api.robinhood.com/instruments/1ca933b1-ec38-45f3-b815-7d2189103133/splits/' },
  margin:
   { initialRatio: 1, dayTradeRatio: 0.25, maintenanceRatio: 1 } }
[ 2019-02-01T00:00:00.000Z,
  2019-02-08T00:00:00.000Z,
  2019-02-15T00:00:00.000Z,
  2019-02-22T00:00:00.000Z,
  2019-03-01T00:00:00.000Z,
  2019-03-08T00:00:00.000Z,
  2019-03-15T00:00:00.000Z,
  2019-04-18T00:00:00.000Z,
  2019-06-21T00:00:00.000Z,
  2019-09-20T00:00:00.000Z,
  2020-01-17T00:00:00.000Z,
  2021-01-15T00:00:00.000Z ]
[]
undefined
(node:22417) UnhandledPromiseRejectionWarning: Error: Object property 'optionInstrument' must be     an instance of the OptionInstrument class
    at _validate     (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:66:54)
    at new OptionOrder     (/home/briansp/node_modules/algotrader/objects/broker/robinhood/OptionOrder.js:29:4)
    at gains (/home/briansp/git/jstrade/test.js:36:15)
    at processTicksAndRejections (internal/process/next_tick.js:81:5)
(node:22417) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error     originated either by throwing inside of an async function without a catch block, or by rejecting a promise     which was not handled with .catch(). (rejection id: 1)
(node:22417) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the     future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit     code.
briansp2020 commented 5 years ago

Ok. Now I see that the way OptionOrder is built has changed. But I'm having problem with getChain which is called before OptionOrder is created.

torreyleonard commented 5 years ago

Out of curiosity, how did you figure out what the new payload should be? I tried using chrome dev tools to look at what information was being sent by the website, but I didn't get anywhere.

I actually took a look at this repo, and it looked like he had options orders working. That way I could save a lot of time by avoiding any network logging.

Ok. Now I see that the way OptionOrder is built has changed. But I'm having problem with getChain which is called before OptionOrder is created.

What error comes up when you try and use getChain? I can't reproduce any issues from that. If you can, open up the OptionInstrument class in your node_modules and add console.log(body) right before return Robinhood.handleResponse - it's on line 81. Then, send me the output and see if it offers any solution. If not, also try console.log(response) and see if that helps. The more information the better!