|
| 1 | +# robinhood.clj |
| 2 | + |
| 3 | +A lightweight clojure wrapper for the [Robinhood Web API](https://github.com/sanko/Robinhood/). |
| 4 | + |
| 5 | +All use cases must follow the [Robinhood TOS]( |
| 6 | +https://brokerage-static.s3.amazonaws.com/assets/robinhood/legal/Robinhood%20Terms%20and%20Conditions.pdf). For example, you may not use this client to scrape Robinhood data for display on your own website, among other things. Read the TOS. |
| 7 | + |
| 8 | +### Authenticating with environment variables |
| 9 | + |
| 10 | +You'll have to set up your machine's environment variables before hitting any endpoints that require authentication. |
| 11 | + |
| 12 | +You can get your device token by monitoring your browser's login request to robinhood. Open chrome developer tools > navigate to network tab > login to Robinhood in browser > search network tab for the `token/` request > view request payload > copy device_token. |
| 13 | + |
| 14 | +``` |
| 15 | +export ROBINHOOD_USER="..." |
| 16 | +export ROBINHOOD_PASS="..." |
| 17 | +export ROBINHOOD_DEVICE_TOKEN="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" |
| 18 | +;Don't forget to restart any running repls |
| 19 | +``` |
| 20 | + |
| 21 | +After adding the above environment variables, `lein test` should run without any failures. |
| 22 | + |
| 23 | +Be sure not to commit any of the above information to this repo for the world to see! If you do, change your passwords immediately and enable 2FA. |
| 24 | + |
| 25 | +### Caution |
| 26 | + |
| 27 | +At the time of writing, the Robinhood Web API lacks official documentation. Hence, working with this wrapper (at least for now) demands some reverse engineering of the robinhood UI. @Sanko's unofficial docs at [Robinhood Web API](https://github.com/sanko/Robinhood/) are good but not 100% up to date. |
| 28 | + |
| 29 | +I've added some notes on how I'm scraping by in the `Dev` section at the bottom. |
| 30 | + |
| 31 | +### REPL Usage |
| 32 | + |
| 33 | +Cd into robinhood.clj and run `lein repl` in order to follow along: |
| 34 | + |
| 35 | +Login: |
| 36 | +``` clojure |
| 37 | +(use 'robinhood.clj.auth) |
| 38 | + |
| 39 | +# Get a robinhood client |
| 40 | +(def rc' (authed-client)) |
| 41 | + |
| 42 | +;; output: |
| 43 | +:robinhood{:access-token "xxxxxxxxx"} |
| 44 | +``` |
| 45 | + |
| 46 | +Fetch Quotes: |
| 47 | +``` clojure |
| 48 | +(use 'robinhood.clj.auth) |
| 49 | +(use 'robinhood.clj.client) |
| 50 | + |
| 51 | +(quotes (authed-client) {:symbols "AAPL,MSFT"}) |
| 52 | + |
| 53 | +;; output: |
| 54 | +(#:robinhood.quote{:updated-at "2021-01-16T01:00:00Z", |
| 55 | + :instrument |
| 56 | + "https://api.robinhood.com/instruments/450dfc6d-5510-4d40-abfb-f633b7d9be3e/", |
| 57 | + :bid-price 126.41, |
| 58 | + :last-trade-price-source "consolidated", |
| 59 | + :symbol "AAPL", |
| 60 | + :last-trade-price 127.14, |
| 61 | + :ask-price 500.0, |
| 62 | + :bid-size 21, |
| 63 | + :ask-size 596, |
| 64 | + :last-extended-hours-trade-price 126.82, |
| 65 | + :previous-close 128.91, |
| 66 | + :has-traded true, |
| 67 | + :trading-halted false, |
| 68 | + :adjusted-previous-close 128.91, |
| 69 | + :previous-close-date "2021-01-14"} |
| 70 | + #:robinhood.quote{:updated-at "2021-01-16T01:00:00Z", |
| 71 | + :instrument |
| 72 | + "https://api.robinhood.com/instruments/50810c35-d215-4866-9758-0ada4ac79ffa/", |
| 73 | + :bid-price 210.91, |
| 74 | + :last-trade-price-source "consolidated", |
| 75 | + :symbol "MSFT", |
| 76 | + :last-trade-price 212.65, |
| 77 | + :ask-price 215.86, |
| 78 | + :bid-size 30, |
| 79 | + :ask-size 200, |
| 80 | + :last-extended-hours-trade-price 212.55, |
| 81 | + :previous-close 213.02, |
| 82 | + :has-traded true, |
| 83 | + :trading-halted false, |
| 84 | + :adjusted-previous-close 213.02, |
| 85 | + :previous-close-date "2021-01-14"}) |
| 86 | +``` |
| 87 | + |
| 88 | + |
| 89 | + |
| 90 | +See call options on a ticker: |
| 91 | +``` clojure |
| 92 | +(use 'robinhood.clj.auth) |
| 93 | +(use 'robinhood.clj.client) |
| 94 | + |
| 95 | +(take 2 ;for brevity |
| 96 | + (get-option-chain-prices (authed-client) {:symbols "EAF"} "call")) |
| 97 | + |
| 98 | +;; output (you'll see some logs of all the requests made): |
| 99 | +({:robinhood.option-contract/bid-size 101, |
| 100 | + :robinhood.option-contract/break-even-price 11.1, |
| 101 | + :robinhood.option-contract/high-price 3.7, |
| 102 | + :robinhood.option-contract/low-price 3.4, |
| 103 | + :robinhood.option-date-chain/id |
| 104 | + "f7e8bfbe-26a1-4300-952b-038d21f332ae", |
| 105 | + :robinhood.option-contract/instrument |
| 106 | + "https://api.robinhood.com/options/instruments/f7e8bfbe-26a1-4300-952b-038d21f332ae/", |
| 107 | + :robinhood.option-date-chain/tradability "tradable", |
| 108 | + :robinhood.option-contract/last-trade-size 10, |
| 109 | + :robinhood.option-contract/high-fill-rate-buy-price 3.74, |
| 110 | + :robinhood.option-date-chain/sellout-datetime |
| 111 | + "2021-07-16T19:00:00+00:00", |
| 112 | + :robinhood.option-contract/adjusted-mark-price 3.6, |
| 113 | + :robinhood.option-contract/bid-price 3.4, |
| 114 | + :robinhood.option-contract/implied-volatility 0.674616, |
| 115 | + :robinhood.option-date-chain/chain-id |
| 116 | + "b0707f5c-839a-44cd-ba9d-a92fb15ab932", |
| 117 | + :robinhood.option-date-chain/url |
| 118 | + "https://api.robinhood.com/options/instruments/f7e8bfbe-26a1-4300-952b-038d21f332ae/", |
| 119 | + :robinhood.option-date-chain/issue-date "2018-05-15", |
| 120 | + :robinhood.option-date-chain/expiration-date "2021-07-16", |
| 121 | + :robinhood.option-date-chain/created-at |
| 122 | + "2020-11-19T04:36:07.713986Z", |
| 123 | + :robinhood.option-contract/delta 0.828468, |
| 124 | + :robinhood.option-contract/low-fill-rate-sell-price 3.62, |
| 125 | + :robinhood.option-contract/high-fill-rate-sell-price 3.45, |
| 126 | + :robinhood.option-contract/chance-of-profit-short 0.637302, |
| 127 | + :robinhood.option-contract/volume 15, |
| 128 | + :robinhood.option-contract/chance-of-profit-long 0.362698, |
| 129 | + :robinhood.option-date-chain/updated-at |
| 130 | + "2020-11-19T04:36:07.713996Z", |
| 131 | + :robinhood.option-date-chain/chain-symbol "EAF", |
| 132 | + :robinhood.option-contract/theta -0.003522, |
| 133 | + :robinhood.option-date-chain/min-ticks |
| 134 | + #:robinhood.option-date-chain.min-ticks{:above-tick 0.1, |
| 135 | + :below-tick 0.05, |
| 136 | + :cutoff-price 3.0}, |
| 137 | + :robinhood.option-contract/vega 0.018886, |
| 138 | + :robinhood.option-contract/open-interest 598, |
| 139 | + :robinhood.option-contract/ask-price 3.8, |
| 140 | + :robinhood.option-date-chain/strike-price 7.5, |
| 141 | + :robinhood.option-contract/mark-price 3.6, |
| 142 | + :robinhood.option-contract/gamma 0.05084, |
| 143 | + :robinhood.option-contract/last-trade-price 3.7, |
| 144 | + :robinhood.option-date-chain/rhs-tradability "untradable", |
| 145 | + :robinhood.option-contract/low-fill-rate-buy-price 3.58, |
| 146 | + :robinhood.option-contract/rho 0.02546, |
| 147 | + :robinhood.option-contract/previous-close-price 4.5, |
| 148 | + :robinhood.option-contract/ask-size 31, |
| 149 | + :robinhood.option-date-chain/type "call", |
| 150 | + :robinhood.option-contract/previous-close-date "2021-01-14", |
| 151 | + :robinhood.option-date-chain/state "active"} |
| 152 | + {:robinhood.option-contract/bid-size 34, |
| 153 | + :robinhood.option-contract/break-even-price 11.38, |
| 154 | + :robinhood.option-contract/high-price 1.4, |
| 155 | + :robinhood.option-contract/low-price 1.33, |
| 156 | + :robinhood.option-date-chain/id |
| 157 | + "e6dec319-3971-4fbb-8f3b-f74ad455b60f", |
| 158 | + :robinhood.option-contract/instrument |
| 159 | + "https://api.robinhood.com/options/instruments/e6dec319-3971-4fbb-8f3b-f74ad455b60f/", |
| 160 | + :robinhood.option-date-chain/tradability "tradable", |
| 161 | + :robinhood.option-contract/last-trade-size 3, |
| 162 | + :robinhood.option-contract/high-fill-rate-buy-price 1.43, |
| 163 | + :robinhood.option-date-chain/sellout-datetime |
| 164 | + "2021-03-19T19:00:00+00:00", |
| 165 | + :robinhood.option-contract/adjusted-mark-price 1.38, |
| 166 | + :robinhood.option-contract/bid-price 1.3, |
| 167 | + :robinhood.option-contract/implied-volatility 0.652445, |
| 168 | + :robinhood.option-date-chain/chain-id |
| 169 | + "b0707f5c-839a-44cd-ba9d-a92fb15ab932", |
| 170 | + :robinhood.option-date-chain/url |
| 171 | + "https://api.robinhood.com/options/instruments/e6dec319-3971-4fbb-8f3b-f74ad455b60f/", |
| 172 | + :robinhood.option-date-chain/issue-date "2018-05-15", |
| 173 | + :robinhood.option-date-chain/expiration-date "2021-03-19", |
| 174 | + :robinhood.option-date-chain/created-at |
| 175 | + "2021-01-14T05:30:06.546505Z", |
| 176 | + :robinhood.option-contract/delta 0.62552, |
| 177 | + :robinhood.option-contract/low-fill-rate-sell-price 1.38, |
| 178 | + :robinhood.option-contract/high-fill-rate-sell-price 1.31, |
| 179 | + :robinhood.option-contract/chance-of-profit-short 0.665686, |
| 180 | + :robinhood.option-contract/volume 6, |
| 181 | + :robinhood.option-contract/chance-of-profit-long 0.334314, |
| 182 | + :robinhood.option-date-chain/updated-at |
| 183 | + "2021-01-14T05:30:06.546519Z", |
| 184 | + :robinhood.option-date-chain/chain-symbol "EAF", |
| 185 | + :robinhood.option-contract/theta -0.008594, |
| 186 | + :robinhood.option-date-chain/min-ticks |
| 187 | + #:robinhood.option-date-chain.min-ticks{:above-tick 0.1, |
| 188 | + :below-tick 0.05, |
| 189 | + :cutoff-price 3.0}, |
| 190 | + :robinhood.option-contract/vega 0.016545, |
| 191 | + :robinhood.option-contract/open-interest 1, |
| 192 | + :robinhood.option-contract/ask-price 1.45, |
| 193 | + :robinhood.option-date-chain/strike-price 10.0, |
| 194 | + :robinhood.option-contract/mark-price 1.375, |
| 195 | + :robinhood.option-contract/gamma 0.13309, |
| 196 | + :robinhood.option-contract/last-trade-price 1.35, |
| 197 | + :robinhood.option-date-chain/rhs-tradability "untradable", |
| 198 | + :robinhood.option-contract/low-fill-rate-buy-price 1.36, |
| 199 | + :robinhood.option-contract/rho 0.008959, |
| 200 | + :robinhood.option-contract/previous-close-price 2.13, |
| 201 | + :robinhood.option-contract/ask-size 49, |
| 202 | + :robinhood.option-date-chain/type "call", |
| 203 | + :robinhood.option-contract/previous-close-date "2021-01-14", |
| 204 | + :robinhood.option-date-chain/state "active"}) |
| 205 | +``` |
| 206 | + |
| 207 | +### dev |
| 208 | + |
| 209 | +My current development flow involves reverse engineering 1 robinhood screen at a time via Chrome's devtools and elbow grease. Whatever works in your browser will work dropped directly into a call to `client/get` or `client/post` (see utils.clj). |
| 210 | + |
| 211 | +Steps to add any Robinhood API endpoint; |
| 212 | + |
| 213 | +1. Find the data I am interested in within chrome dev tools (the network tools bar is great for this, especially via the filter bar > `Find all`). |
| 214 | +2. Click any name in the list of network requests. (PROTIP: robinhood makes loads of requests; cycle your selection here with up/down arrow keys.) |
| 215 | +3. Look at the `Headers` > Scroll to bottom > Open `Request Headers` if its closed. |
| 216 | +4. Go ahead and copy/paste this into robinhood.clj/utils.clj for some experimentation |
| 217 | +5. If you copied a `GET`, try to get a bare call to [`clj-http.client/get`](https://github.com/dakrone/clj-http#get) to work (use `clj-http.client/post` for a `POST`!) |
| 218 | +6. Transform the working `client/get` call block (with static data) into a function by allowing the passing of a parameter in place of each dynamic field. |
| 219 | +7. Trace information to its source calls by searching for unique strings/numbers back in the network-tab/filter-bar. |
| 220 | +8. If any of the source calls that pull prerequisite data are not yet implemented, then wash/rinse/repeat. |
| 221 | + |
| 222 | +TODO; add to clojars, then add installation & usage instructs |
0 commit comments