intrinio-realtime 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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