robinhood-api 0.4.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/robinhood/api.rb +232 -0
- metadata +4 -4
- data/lib/robinhood.rb +0 -249
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 81ac293079b6363a879823566ebb18d622c40882f5a9a66ed1d62569ae12b288
|
4
|
+
data.tar.gz: ab0fe7c48885d4a2489629b37edec1f31b25679b0b5289be3c559c1e01f28e8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c99809c83fc0abcaac0e36625db1b8f7e7f5c8d0ba75016136b2c828d481fb0c3b66c2b35cc8c454eee16c349820c44d26ae9e574f3ed105e49b67f4d7ce779a
|
7
|
+
data.tar.gz: 053762a01c049948191ad17bc61baf903fb4d16b59d2f20057f603af7608510a2795feb6754ba26498fa8756881ccfac29c6f0982b7b808f26603195285b914e
|
@@ -0,0 +1,232 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Robinhood
|
4
|
+
# The API module check for methods that require login.
|
5
|
+
module ApiModule
|
6
|
+
# Your code goes here...
|
7
|
+
def self.before(*names)
|
8
|
+
names.each do |name|
|
9
|
+
m = instance_method(name)
|
10
|
+
define_method(name) do |*args, &block|
|
11
|
+
if token_compliant?(name)
|
12
|
+
m.bind(self).call(*args, &block)
|
13
|
+
else
|
14
|
+
puts 'You have to run the login(<username>, <password>) method ' \
|
15
|
+
"or a Robinhood.new instance before running the #{name} method"
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# The API module instance methods
|
24
|
+
module ApiModule
|
25
|
+
require 'httparty'
|
26
|
+
require 'json'
|
27
|
+
|
28
|
+
require_relative './api/orders'
|
29
|
+
|
30
|
+
include HTTParty
|
31
|
+
|
32
|
+
attr_accessor :errors
|
33
|
+
|
34
|
+
def initialize; end
|
35
|
+
|
36
|
+
def login(username, password)
|
37
|
+
raw_response = HTTParty.post(
|
38
|
+
endpoints[:login],
|
39
|
+
body: {
|
40
|
+
'password' => password,
|
41
|
+
'username' => username
|
42
|
+
},
|
43
|
+
headers: headers
|
44
|
+
)
|
45
|
+
response = JSON.parse(raw_response.body)
|
46
|
+
if response['token']
|
47
|
+
response = response['token']
|
48
|
+
@headers['Authorization'] = "Token #{response}"
|
49
|
+
end
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
def investment_profile
|
54
|
+
raw_response = HTTParty.get(
|
55
|
+
endpoints[:investment_profile], headers: headers
|
56
|
+
)
|
57
|
+
JSON.parse(raw_response.body)
|
58
|
+
end
|
59
|
+
|
60
|
+
def accounts
|
61
|
+
raw_response = HTTParty.get(endpoints[:accounts], headers: headers)
|
62
|
+
JSON.parse(raw_response.body)
|
63
|
+
end
|
64
|
+
|
65
|
+
def portfolio(account_number)
|
66
|
+
raw_response = HTTParty.get(
|
67
|
+
"https://api.robinhood.com/accounts/#{account_number}/portfolio/",
|
68
|
+
headers: headers
|
69
|
+
)
|
70
|
+
JSON.parse(raw_response.body)
|
71
|
+
end
|
72
|
+
|
73
|
+
def instruments(symbol)
|
74
|
+
if symbol.include?('-')
|
75
|
+
raw_response = HTTParty.get(
|
76
|
+
"#{endpoints[:instruments]}#{symbol}/", headers: headers
|
77
|
+
)
|
78
|
+
else
|
79
|
+
raw_response = HTTParty.get(
|
80
|
+
endpoints[:instruments],
|
81
|
+
query: { 'query' => symbol.upcase },
|
82
|
+
headers: headers
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
JSON.parse(raw_response.body)
|
87
|
+
end
|
88
|
+
|
89
|
+
def quote(symbol)
|
90
|
+
raw_response = HTTParty.get(
|
91
|
+
"https://api.robinhood.com/quotes/#{symbol}/",
|
92
|
+
headers: headers
|
93
|
+
)
|
94
|
+
JSON.parse(raw_response.body)
|
95
|
+
end
|
96
|
+
|
97
|
+
def positions(account_number, instrument_id = nil)
|
98
|
+
url = "https://api.robinhood.com/accounts/#{account_number}/positions"
|
99
|
+
url = "#{url}/#{instrument_id}/" if instrument_id
|
100
|
+
raw_response = HTTParty.get(url, headers: headers)
|
101
|
+
JSON.parse(raw_response.body)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def endpoints
|
107
|
+
{
|
108
|
+
login: 'https://api.robinhood.com/api-token-auth/',
|
109
|
+
investment_profile: 'https://api.robinhood.com/user/investment_profile/',
|
110
|
+
accounts: 'https://api.robinhood.com/accounts/',
|
111
|
+
ach_iav_auth: 'https://api.robinhood.com/ach/iav/auth/',
|
112
|
+
ach_relationships: 'https://api.robinhood.com/ach/relationships/',
|
113
|
+
ach_transfers: 'https://api.robinhood.com/ach/transfers/',
|
114
|
+
applications: 'https://api.robinhood.com/applications/',
|
115
|
+
dividends: 'https://api.robinhood.com/dividends/',
|
116
|
+
edocuments: 'https://api.robinhood.com/documents/',
|
117
|
+
instruments: 'https://api.robinhood.com/instruments/',
|
118
|
+
margin_upgrade: 'https://api.robinhood.com/margin/upgrades/',
|
119
|
+
markets: 'https://api.robinhood.com/markets/',
|
120
|
+
notifications: 'https://api.robinhood.com/notifications/',
|
121
|
+
orders: 'https://api.robinhood.com/orders/',
|
122
|
+
password_reset: 'https://api.robinhood.com/password_reset/request/',
|
123
|
+
quotes: 'https://api.robinhood.com/quotes/',
|
124
|
+
document_requests: 'https://api.robinhood.com/upload/document_requests/',
|
125
|
+
user: 'https://api.robinhood.com/user/',
|
126
|
+
watchlists: 'https://api.robinhood.com/watchlists/'
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
def methodlist
|
131
|
+
%i[investment_profile accounts ach_iav_auth ach_relationships
|
132
|
+
ach_transfers applications dividends edocuments margin_upgrade
|
133
|
+
notifications orders password_reset document_requests user watchlists]
|
134
|
+
end
|
135
|
+
|
136
|
+
def headers
|
137
|
+
@headers ||= {
|
138
|
+
'Accept' => 'application/json'
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
def token_compliant?(method)
|
143
|
+
if methodlist.include?(method)
|
144
|
+
if headers.key?('Authorization')
|
145
|
+
true
|
146
|
+
else
|
147
|
+
false
|
148
|
+
end
|
149
|
+
else
|
150
|
+
true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# before(*instance_methods) { puts 'start' }
|
155
|
+
end
|
156
|
+
|
157
|
+
# Robinhood API's class methods
|
158
|
+
class Api
|
159
|
+
include ApiModule
|
160
|
+
|
161
|
+
def self.flatten(var)
|
162
|
+
new_var = {}
|
163
|
+
var.each do |k, v|
|
164
|
+
if v.class == Hash
|
165
|
+
v.each do |l, w|
|
166
|
+
new_var[l] = w
|
167
|
+
end
|
168
|
+
else
|
169
|
+
new_var[k] = v
|
170
|
+
end
|
171
|
+
end
|
172
|
+
new_var
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.sanitize(var)
|
176
|
+
var.delete('updated_at') if var.keys.include?('updated_at')
|
177
|
+
|
178
|
+
var.delete('created_at') if var.keys.include?('created_at')
|
179
|
+
|
180
|
+
new_var = {}
|
181
|
+
var.each do |k, v|
|
182
|
+
key = k
|
183
|
+
key = 'object_type' if key == 'type'
|
184
|
+
key = 'third_party_id' if key == 'id'
|
185
|
+
if v.class == String
|
186
|
+
if v.include?('T') && v.include?('Z')
|
187
|
+
begin
|
188
|
+
new_var[key] = Time.parse(v)
|
189
|
+
rescue StandardError
|
190
|
+
new_var[key] = v
|
191
|
+
end
|
192
|
+
else
|
193
|
+
begin
|
194
|
+
new_var[key] = Float(v)
|
195
|
+
rescue StandardError
|
196
|
+
new_var[key] = v
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
new_var
|
202
|
+
end
|
203
|
+
|
204
|
+
def self.strip(str)
|
205
|
+
str.split('/').last
|
206
|
+
end
|
207
|
+
|
208
|
+
def self.fix(var)
|
209
|
+
if var.class == Hash
|
210
|
+
fix_hash(var)
|
211
|
+
elsif var.class == Array
|
212
|
+
arr = []
|
213
|
+
var.each do |ha|
|
214
|
+
arr << fix_hash(ha)
|
215
|
+
end
|
216
|
+
arr
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.fix_hash(var)
|
221
|
+
instance = var.clone
|
222
|
+
instance = flatten(instance)
|
223
|
+
instance = sanitize(instance)
|
224
|
+
keys = %w[account instrument position quote market fundamentals]
|
225
|
+
keys.each do |key|
|
226
|
+
instance[key] = strip(instance[key]) if instance.key?(key)
|
227
|
+
end
|
228
|
+
|
229
|
+
instance
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: robinhood-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Alcala
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: 'Get the most of Robinhood: accounts, positions, portfolio, buy and sell
|
14
14
|
securities, etc.'
|
@@ -17,7 +17,7 @@ executables: []
|
|
17
17
|
extensions: []
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
|
-
- lib/robinhood.rb
|
20
|
+
- lib/robinhood/api.rb
|
21
21
|
homepage: https://github.com/ThomasMarcel/robinhood-api
|
22
22
|
licenses:
|
23
23
|
- MIT
|
@@ -38,7 +38,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
38
38
|
version: '0'
|
39
39
|
requirements: []
|
40
40
|
rubyforge_project:
|
41
|
-
rubygems_version: 2.6
|
41
|
+
rubygems_version: 2.7.6
|
42
42
|
signing_key:
|
43
43
|
specification_version: 4
|
44
44
|
summary: Calling the Robinhood API
|
data/lib/robinhood.rb
DELETED
@@ -1,249 +0,0 @@
|
|
1
|
-
module RobinhoodModule
|
2
|
-
def self.before(*names)
|
3
|
-
names.each do |name|
|
4
|
-
m = instance_method(name)
|
5
|
-
define_method(name) do |*args, &block|
|
6
|
-
if token_compliant?(name)
|
7
|
-
m.bind(self).(*args, &block)
|
8
|
-
else
|
9
|
-
puts "You have to run the login(<username>, <password>) method or a Robinhood.new instance before running the #{name} method"
|
10
|
-
exit 1
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
module RobinhoodModule
|
18
|
-
require 'httparty'
|
19
|
-
require 'json'
|
20
|
-
|
21
|
-
include HTTParty
|
22
|
-
|
23
|
-
attr_accessor :errors
|
24
|
-
|
25
|
-
def initialize
|
26
|
-
end
|
27
|
-
|
28
|
-
def login(username, password)
|
29
|
-
raw_response = HTTParty.post(
|
30
|
-
endpoints[:login],
|
31
|
-
body: {
|
32
|
-
'password' => password,
|
33
|
-
'username'=> username
|
34
|
-
},
|
35
|
-
headers: headers
|
36
|
-
)
|
37
|
-
response = JSON.parse(raw_response.body)
|
38
|
-
if response['token']
|
39
|
-
response = response['token']
|
40
|
-
@headers['Authorization'] = "Token #{response}"
|
41
|
-
end
|
42
|
-
return response
|
43
|
-
end
|
44
|
-
|
45
|
-
def investment_profile
|
46
|
-
raw_response = HTTParty.get(endpoints[:investment_profile], headers: headers)
|
47
|
-
JSON.parse(raw_response.body)
|
48
|
-
end
|
49
|
-
|
50
|
-
def orders(order_id=nil)
|
51
|
-
url = order_id ? "#{endpoints[:orders]}#{order_id}" : endpoints[:orders]
|
52
|
-
raw_response = HTTParty.get(url, headers: headers)
|
53
|
-
JSON.parse(raw_response.body)
|
54
|
-
end
|
55
|
-
|
56
|
-
def accounts
|
57
|
-
raw_response = HTTParty.get(endpoints[:accounts], headers: headers)
|
58
|
-
JSON.parse(raw_response.body)
|
59
|
-
end
|
60
|
-
|
61
|
-
def portfolio(account_number)
|
62
|
-
raw_response = HTTParty.get("https://api.robinhood.com/accounts/#{account_number}/portfolio/", headers: headers)
|
63
|
-
JSON.parse(raw_response.body)
|
64
|
-
end
|
65
|
-
|
66
|
-
def instruments(symbol)
|
67
|
-
if symbol.include?('-')
|
68
|
-
raw_response = HTTParty.get("#{endpoints[:instruments]}#{symbol}/", headers: headers)
|
69
|
-
else
|
70
|
-
raw_response = HTTParty.get(endpoints[:instruments], query: {'query' => symbol.upcase}, headers: headers)
|
71
|
-
end
|
72
|
-
|
73
|
-
JSON.parse(raw_response.body)
|
74
|
-
end
|
75
|
-
|
76
|
-
def quote(symbol)
|
77
|
-
raw_response = HTTParty.get("https://api.robinhood.com/quotes/#{symbol}/", headers: headers)
|
78
|
-
JSON.parse(raw_response.body)
|
79
|
-
end
|
80
|
-
|
81
|
-
def buy(account_number, symbol, instrument_id, price, quantity)
|
82
|
-
raw_response = HTTParty.post(
|
83
|
-
endpoints[:orders],
|
84
|
-
body: {
|
85
|
-
'account' => "https://api.robinhood.com/accounts/#{account_number}/",
|
86
|
-
'instrument' => "https://api.robinhood.com/instruments/#{instrument_id}/",
|
87
|
-
'price' => price,
|
88
|
-
'quantity' => quantity,
|
89
|
-
'side' => "buy",
|
90
|
-
'symbol' => symbol,
|
91
|
-
'time_in_force' => 'gfd',
|
92
|
-
'trigger' => 'immediate',
|
93
|
-
'type' => 'market'
|
94
|
-
},
|
95
|
-
headers: headers
|
96
|
-
)
|
97
|
-
|
98
|
-
JSON.parse(raw_response.body)
|
99
|
-
end
|
100
|
-
|
101
|
-
def limit_buy(account_number, symbol, instrument_id, price, quantity)
|
102
|
-
raw_response = HTTParty.post(
|
103
|
-
endpoints[:orders],
|
104
|
-
body: {
|
105
|
-
'account' => "https://api.robinhood.com/accounts/#{account_number}/",
|
106
|
-
'instrument' => "https://api.robinhood.com/instruments/#{instrument_id}/",
|
107
|
-
'price' => price,
|
108
|
-
'quantity' => quantity,
|
109
|
-
'side' => "buy",
|
110
|
-
'symbol' => symbol,
|
111
|
-
'time_in_force' => 'gfd',
|
112
|
-
'trigger' => 'immediate',
|
113
|
-
'type' => 'limit'
|
114
|
-
}.to_json,
|
115
|
-
headers: headers
|
116
|
-
)
|
117
|
-
|
118
|
-
JSON.parse(raw_response.body)
|
119
|
-
end
|
120
|
-
|
121
|
-
def sell(account_number, symbol, instrument_id, price, quantity)
|
122
|
-
raw_response = HTTParty.post(
|
123
|
-
endpoints[:orders],
|
124
|
-
body: {
|
125
|
-
'account' => "https://api.robinhood.com/accounts/#{account_number}/",
|
126
|
-
'instrument' => "https://api.robinhood.com/instruments/#{instrument_id}/",
|
127
|
-
'price' => price,
|
128
|
-
'quantity' => quantity,
|
129
|
-
'side' => "sell",
|
130
|
-
'symbol' => symbol,
|
131
|
-
'time_in_force' => 'gfd',
|
132
|
-
'trigger' => 'immediate',
|
133
|
-
'type' => 'market'
|
134
|
-
},
|
135
|
-
headers: headers
|
136
|
-
)
|
137
|
-
|
138
|
-
JSON.parse(raw_response.body)
|
139
|
-
end
|
140
|
-
|
141
|
-
def limit_sell(account_number, symbol, instrument_id, price, quantity)
|
142
|
-
raw_response = HTTParty.post(
|
143
|
-
endpoints[:orders],
|
144
|
-
body: {
|
145
|
-
'account' => "https://api.robinhood.com/accounts/#{account_number}/",
|
146
|
-
'instrument' => "https://api.robinhood.com/instruments/#{instrument_id}/",
|
147
|
-
'price' => price,
|
148
|
-
'quantity' => quantity,
|
149
|
-
'side' => "sell",
|
150
|
-
'symbol' => symbol,
|
151
|
-
'time_in_force' => 'gfd',
|
152
|
-
'trigger' => 'immediate',
|
153
|
-
'type' => 'limit'
|
154
|
-
}.to_json,
|
155
|
-
headers: headers
|
156
|
-
)
|
157
|
-
|
158
|
-
JSON.parse(raw_response.body)
|
159
|
-
end
|
160
|
-
|
161
|
-
def stop_loss_sell(account_number, symbol, instrument_id, price, quantity)
|
162
|
-
raw_response = HTTParty.post(
|
163
|
-
endpoints[:orders],
|
164
|
-
body: {
|
165
|
-
'account' => "https://api.robinhood.com/accounts/#{account_number}/",
|
166
|
-
'instrument' => "https://api.robinhood.com/instruments/#{instrument_id}/",
|
167
|
-
'stop_price' => price,
|
168
|
-
'quantity' => quantity,
|
169
|
-
'side' => "sell",
|
170
|
-
'symbol' => symbol,
|
171
|
-
'time_in_force' => 'gtc',
|
172
|
-
'trigger' => 'stop',
|
173
|
-
'type' => 'market'
|
174
|
-
}.to_json,
|
175
|
-
headers: headers
|
176
|
-
)
|
177
|
-
|
178
|
-
JSON.parse(raw_response.body)
|
179
|
-
end
|
180
|
-
|
181
|
-
def cancel_order(order_id)
|
182
|
-
raw_response = HTTParty.post("https://api.robinhood.com/orders/#{order_id}/cancel/", headers: headers)
|
183
|
-
raw_response.code == 200
|
184
|
-
end
|
185
|
-
|
186
|
-
def positions(account_number, instrument_id = nil)
|
187
|
-
url = "https://api.robinhood.com/accounts/#{account_number}/positions"
|
188
|
-
if instrument_id
|
189
|
-
url = "#{url}/#{instrument_id}/"
|
190
|
-
end
|
191
|
-
raw_response = HTTParty.get(url, headers: headers)
|
192
|
-
JSON.parse(raw_response.body)
|
193
|
-
end
|
194
|
-
|
195
|
-
private
|
196
|
-
|
197
|
-
def endpoints
|
198
|
-
{
|
199
|
-
login: 'https://api.robinhood.com/api-token-auth/',
|
200
|
-
investment_profile: 'https://api.robinhood.com/user/investment_profile/',
|
201
|
-
accounts: 'https://api.robinhood.com/accounts/',
|
202
|
-
ach_iav_auth: 'https://api.robinhood.com/ach/iav/auth/',
|
203
|
-
ach_relationships: 'https://api.robinhood.com/ach/relationships/',
|
204
|
-
ach_transfers: 'https://api.robinhood.com/ach/transfers/',
|
205
|
-
applications: 'https://api.robinhood.com/applications/',
|
206
|
-
dividends: 'https://api.robinhood.com/dividends/',
|
207
|
-
edocuments: 'https://api.robinhood.com/documents/',
|
208
|
-
instruments: 'https://api.robinhood.com/instruments/',
|
209
|
-
margin_upgrade: 'https://api.robinhood.com/margin/upgrades/',
|
210
|
-
markets: 'https://api.robinhood.com/markets/',
|
211
|
-
notifications: 'https://api.robinhood.com/notifications/',
|
212
|
-
orders: 'https://api.robinhood.com/orders/',
|
213
|
-
password_reset: 'https://api.robinhood.com/password_reset/request/',
|
214
|
-
quotes: 'https://api.robinhood.com/quotes/',
|
215
|
-
document_requests: 'https://api.robinhood.com/upload/document_requests/',
|
216
|
-
user: 'https://api.robinhood.com/user/',
|
217
|
-
watchlists: 'https://api.robinhood.com/watchlists/'
|
218
|
-
}
|
219
|
-
end
|
220
|
-
|
221
|
-
def methodlist
|
222
|
-
[:investment_profile, :accounts, :ach_iav_auth, :ach_relationships, :ach_transfers, :applications, :dividends, :edocuments, :margin_upgrade, :notifications, :orders, :password_reset, :document_requests, :user, :watchlists]
|
223
|
-
end
|
224
|
-
|
225
|
-
def headers
|
226
|
-
@headers ||= {
|
227
|
-
'Accept' => 'application/json',
|
228
|
-
}
|
229
|
-
end
|
230
|
-
|
231
|
-
def token_compliant?(method)
|
232
|
-
|
233
|
-
if methodlist.include?(method)
|
234
|
-
if headers.key?('Authorization')
|
235
|
-
return true
|
236
|
-
else
|
237
|
-
return false
|
238
|
-
end
|
239
|
-
else
|
240
|
-
return true
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
before(*instance_methods) { puts "start" }
|
245
|
-
end
|
246
|
-
|
247
|
-
class Robinhood
|
248
|
-
include RobinhoodModule
|
249
|
-
end
|