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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/intrinio-realtime.rb +109 -36
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5bfe9d4ae794df6de84bdc415554e2562da3369
4
- data.tar.gz: 43a51886bdf1fe2603b5af0420919945325a58d1
3
+ metadata.gz: d6c6b1e8ef7761659187a4f70cfd2d63b22a6bfb
4
+ data.tar.gz: 1ce5ed2d02a98101de4f36e6ba8b7a0db0bc1868
5
5
  SHA512:
6
- metadata.gz: 3d356ae56916d72fb0cc0243e58354ec925b08e598fd97aa121aaeb80fb07fdb1be61cc60e78e95a1c1c120635acaf70db0ea5d1ba6a46c546051d034d62950f
7
- data.tar.gz: 2a210412771750e5ed2ac3cfc17fd210c498dd2a88537f15981ded4c3c7b4e73ad8d4e1d2740289ec6dda1aeb3a6a988f22f518f33c3f26bc868c3257452583d
6
+ metadata.gz: 6555c857b6e00546c9d9fa6d237a74fda2cb410803dd2ff6c8fcf648be9da1cbb60cb2d6fb4eb49bb987541b16363c3b2cd28073ccf8873e7e177f28e725f0ab
7
+ data.tar.gz: a2392977884efb471d1db6fd42f1093fddfe4f0b68e02e63194aab3b560e286ae464a15ffffaa2ee3cc710b248844822e8e6a8db7cb76aa1441d373fae269413
@@ -6,12 +6,11 @@ require 'websocket-client-simple'
6
6
 
7
7
  module Intrinio
8
8
  module Realtime
9
- AUTH_URL = "https://realtime.intrinio.com/auth"
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
- DEFAULT_POOL_SIZE = 100
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(AUTH_URL)
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
- URI.escape(SOCKET_URL + "?vsn=1.0.0&token=#{@token}")
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
- if json["event"] == "quote"
158
- quote = json["payload"]
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
- topic: parse_topic(channel),
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
- topic: parse_topic(channel),
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
- debug "Heartbeat"
214
- @ws.send(HEARTBEAT_MSG)
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 parse_topic(channel)
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 parse_channels(channels)
288
- channels.flatten!
289
- channels.uniq!
290
- channels.compact!
291
- channels
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: 1.0.0
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-05-11 00:00:00.000000000 Z
11
+ date: 2017-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http