intrinio-realtime 1.0.0 → 2.0.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 +4 -4
- data/lib/intrinio-realtime.rb +109 -36
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6c6b1e8ef7761659187a4f70cfd2d63b22a6bfb
|
4
|
+
data.tar.gz: 1ce5ed2d02a98101de4f36e6ba8b7a0db0bc1868
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6555c857b6e00546c9d9fa6d237a74fda2cb410803dd2ff6c8fcf648be9da1cbb60cb2d6fb4eb49bb987541b16363c3b2cd28073ccf8873e7e177f28e725f0ab
|
7
|
+
data.tar.gz: a2392977884efb471d1db6fd42f1093fddfe4f0b68e02e63194aab3b560e286ae464a15ffffaa2ee3cc710b248844822e8e6a8db7cb76aa1441d373fae269413
|
data/lib/intrinio-realtime.rb
CHANGED
@@ -6,12 +6,11 @@ require 'websocket-client-simple'
|
|
6
6
|
|
7
7
|
module Intrinio
|
8
8
|
module Realtime
|
9
|
-
|
10
|
-
SOCKET_URL = "wss://realtime.intrinio.com/socket/websocket"
|
11
|
-
HEARTBEAT_TIME = 1
|
12
|
-
HEARTBEAT_MSG = {topic: 'phoenix', event: 'heartbeat', payload: {}, ref: nil}.to_json
|
9
|
+
HEARTBEAT_TIME = 3
|
13
10
|
SELF_HEAL_BACKOFFS = [0,100,500,1000,2000,5000]
|
14
|
-
|
11
|
+
IEX = "iex"
|
12
|
+
QUODD = "quodd"
|
13
|
+
PROVIDERS = [IEX, QUODD]
|
15
14
|
|
16
15
|
def self.connect(options, &b)
|
17
16
|
EM.run do
|
@@ -29,6 +28,9 @@ module Intrinio
|
|
29
28
|
@password = options[:password]
|
30
29
|
raise "Username and password are required" if @username.nil? || @username.empty? || @password.nil? || @password.empty?
|
31
30
|
|
31
|
+
@provider = options[:provider]
|
32
|
+
raise "Provider must be 'quodd' or 'iex'" unless PROVIDERS.include?(@provider)
|
33
|
+
|
32
34
|
@channels = []
|
33
35
|
@channels = parse_channels(options[:channels]) if options[:channels]
|
34
36
|
bad_channels = @channels.select{|x| !x.is_a?(String)}
|
@@ -52,6 +54,10 @@ module Intrinio
|
|
52
54
|
@ws = nil
|
53
55
|
end
|
54
56
|
|
57
|
+
def provider
|
58
|
+
@provider
|
59
|
+
end
|
60
|
+
|
55
61
|
def join(*channels)
|
56
62
|
channels = parse_channels(channels)
|
57
63
|
nonconforming = channels.select{|x| !x.is_a?(String)}
|
@@ -97,7 +103,7 @@ module Intrinio
|
|
97
103
|
refresh_token()
|
98
104
|
refresh_websocket()
|
99
105
|
rescue StandardError => e
|
100
|
-
error("Connection error: #{e}")
|
106
|
+
error("Connection error: #{e} \n#{e.backtrace.join("\n")}")
|
101
107
|
try_self_heal()
|
102
108
|
end
|
103
109
|
end
|
@@ -119,7 +125,7 @@ module Intrinio
|
|
119
125
|
def refresh_token
|
120
126
|
@token = nil
|
121
127
|
|
122
|
-
response = HTTP.basic_auth(:user => @username, :pass => @password).get(
|
128
|
+
response = HTTP.basic_auth(:user => @username, :pass => @password).get(auth_url)
|
123
129
|
return fatal("Unable to authorize") if response.status == 401
|
124
130
|
return fatal("Could not get auth token") if response.status != 200
|
125
131
|
|
@@ -127,8 +133,18 @@ module Intrinio
|
|
127
133
|
debug "Token refreshed"
|
128
134
|
end
|
129
135
|
|
136
|
+
def auth_url
|
137
|
+
case @provider
|
138
|
+
when IEX then "https://realtime.intrinio.com/auth"
|
139
|
+
when QUODD then "https://api.intrinio.com/token?type=QUODD"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
130
143
|
def socket_url
|
131
|
-
|
144
|
+
case @provider
|
145
|
+
when IEX then URI.escape("wss://realtime.intrinio.com/socket/websocket?vsn=1.0.0&token=#{@token}")
|
146
|
+
when QUODD then URI.escape("wss://www5.quodd.com/websocket/webStreamer/intrinio/#{@token}")
|
147
|
+
end
|
132
148
|
end
|
133
149
|
|
134
150
|
def refresh_websocket
|
@@ -139,12 +155,15 @@ module Intrinio
|
|
139
155
|
@joined_channels = []
|
140
156
|
|
141
157
|
@ws = ws = WebSocket::Client::Simple.connect(socket_url)
|
158
|
+
me.send :info, "Connection opening"
|
142
159
|
|
143
160
|
ws.on :open do
|
144
|
-
me.send :ready, true
|
145
161
|
me.send :info, "Connection established"
|
162
|
+
me.send :ready, true
|
163
|
+
if me.send(:provider) == IEX
|
164
|
+
me.send :refresh_channels
|
165
|
+
end
|
146
166
|
me.send :start_heartbeat
|
147
|
-
me.send :refresh_channels
|
148
167
|
me.send :stop_self_heal
|
149
168
|
end
|
150
169
|
|
@@ -154,14 +173,31 @@ module Intrinio
|
|
154
173
|
|
155
174
|
begin
|
156
175
|
json = JSON.parse(message)
|
157
|
-
|
158
|
-
|
176
|
+
|
177
|
+
quote = case me.send(:provider)
|
178
|
+
when IEX
|
179
|
+
if json["event"] == "quote"
|
180
|
+
json["payload"]
|
181
|
+
end
|
182
|
+
when QUODD
|
183
|
+
if json["event"] == "info" && json["data"]["message"] == "Connected"
|
184
|
+
me.send :refresh_channels
|
185
|
+
elsif json["event"] == "quote" || json["event"] == "trade"
|
186
|
+
json["data"]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
if quote && quote.is_a?(Hash)
|
159
191
|
me.send :process_quote, quote
|
160
192
|
end
|
161
193
|
rescue StandardError => e
|
162
194
|
me.send :error, "Could not parse message: #{message} #{e}"
|
163
195
|
end
|
164
196
|
end
|
197
|
+
|
198
|
+
ws.on :close do |e|
|
199
|
+
me.send :disconnect
|
200
|
+
end
|
165
201
|
|
166
202
|
ws.on :error do |e|
|
167
203
|
me.send :ready, false
|
@@ -177,28 +213,16 @@ module Intrinio
|
|
177
213
|
# Join new channels
|
178
214
|
new_channels = @channels - @joined_channels
|
179
215
|
new_channels.each do |channel|
|
180
|
-
msg =
|
181
|
-
|
182
|
-
event: "phx_join",
|
183
|
-
payload: {},
|
184
|
-
ref: nil
|
185
|
-
}.to_json
|
186
|
-
|
187
|
-
@ws.send(msg)
|
216
|
+
msg = join_message(channel)
|
217
|
+
@ws.send(msg.to_json)
|
188
218
|
info "Joined #{channel}"
|
189
219
|
end
|
190
220
|
|
191
221
|
# Leave old channels
|
192
222
|
old_channels = @joined_channels - @channels
|
193
223
|
old_channels.each do |channel|
|
194
|
-
msg =
|
195
|
-
|
196
|
-
event: 'phx_leave',
|
197
|
-
payload: {},
|
198
|
-
ref: nil
|
199
|
-
}.to_json
|
200
|
-
|
201
|
-
@ws.send(msg)
|
224
|
+
msg = leave_message(channel)
|
225
|
+
@ws.send(msg.to_json)
|
202
226
|
info "Left #{channel}"
|
203
227
|
end
|
204
228
|
|
@@ -210,8 +234,17 @@ module Intrinio
|
|
210
234
|
def start_heartbeat
|
211
235
|
EM.cancel_timer(@heartbeat_timer) if @heartbeat_timer
|
212
236
|
@heartbeat_timer = EM.add_periodic_timer(HEARTBEAT_TIME) do
|
213
|
-
|
214
|
-
|
237
|
+
if msg = heartbeat_msg()
|
238
|
+
@ws.send(msg)
|
239
|
+
debug "Heartbeat #{msg}"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def heartbeat_msg
|
245
|
+
case @provider
|
246
|
+
when IEX then {topic: 'phoenix', event: 'heartbeat', payload: {}, ref: nil}.to_json
|
247
|
+
when QUODD then {event: 'heartbeat', data: {action: 'heartbeat', ticker: (Time.now.to_f * 1000).to_i}}.to_json
|
215
248
|
end
|
216
249
|
end
|
217
250
|
|
@@ -273,7 +306,14 @@ module Intrinio
|
|
273
306
|
nil
|
274
307
|
end
|
275
308
|
|
276
|
-
def
|
309
|
+
def parse_channels(channels)
|
310
|
+
channels.flatten!
|
311
|
+
channels.uniq!
|
312
|
+
channels.compact!
|
313
|
+
channels
|
314
|
+
end
|
315
|
+
|
316
|
+
def parse_iex_topic(channel)
|
277
317
|
case channel
|
278
318
|
when "$lobby"
|
279
319
|
"iex:lobby"
|
@@ -284,11 +324,44 @@ module Intrinio
|
|
284
324
|
end
|
285
325
|
end
|
286
326
|
|
287
|
-
def
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
327
|
+
def join_message(channel)
|
328
|
+
case @provider
|
329
|
+
when IEX
|
330
|
+
{
|
331
|
+
topic: parse_iex_topic(channel),
|
332
|
+
event: "phx_join",
|
333
|
+
payload: {},
|
334
|
+
ref: nil
|
335
|
+
}
|
336
|
+
when QUODD
|
337
|
+
{
|
338
|
+
event: "subscribe",
|
339
|
+
data: {
|
340
|
+
ticker: channel,
|
341
|
+
action: "subscribe"
|
342
|
+
}
|
343
|
+
}
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def leave_message(channel)
|
348
|
+
case @provider
|
349
|
+
when IEX
|
350
|
+
{
|
351
|
+
topic: parse_iex_topic(channel),
|
352
|
+
event: "phx_leave",
|
353
|
+
payload: {},
|
354
|
+
ref: nil
|
355
|
+
}
|
356
|
+
when QUODD
|
357
|
+
{
|
358
|
+
event: "unsubscribe",
|
359
|
+
data: {
|
360
|
+
ticker: channel,
|
361
|
+
action: "unsubscribe"
|
362
|
+
}
|
363
|
+
}
|
364
|
+
end
|
292
365
|
end
|
293
366
|
end
|
294
367
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: intrinio-realtime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Intrinio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http
|