intrinio-realtime 2.1.1 → 3.1.2
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/intrinio-realtime.rb +275 -132
- metadata +33 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 230f6b829df617dbc89057d3b413e8e72ab9f1ce54cbfd40962beba8649b7fc8
|
4
|
+
data.tar.gz: f4f9ddf1279d94b2533ff37badd573c99323db4ad03b513251a3c688ab196bee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b07b66332b3e1ed14c506db140e7d3bd87d239c9e8868663a53442b76ec34178ee57add42bc13549e085cfbfad2cdf86927aa00f92b619a532feda517fdebb49
|
7
|
+
data.tar.gz: e1696ccdc4a6e4c037c84dd7f9e0ac5def100d974cdf286dad45a098a6cdf0007eed911237ae698b975f37941eddffb2e59947d0bab698ac2fddd78fd230671d
|
data/lib/intrinio-realtime.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'logger'
|
2
2
|
require 'uri'
|
3
|
-
require 'http'
|
3
|
+
#require 'http'
|
4
|
+
require 'net/http'
|
4
5
|
require 'eventmachine'
|
5
6
|
require 'websocket-client-simple'
|
6
7
|
|
@@ -8,27 +9,100 @@ module Intrinio
|
|
8
9
|
module Realtime
|
9
10
|
HEARTBEAT_TIME = 3
|
10
11
|
SELF_HEAL_BACKOFFS = [0, 100, 500, 1000, 2000, 5000].freeze
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
REALTIME = "REALTIME".freeze
|
13
|
+
MANUAL = "MANUAL".freeze
|
14
|
+
PROVIDERS = [REALTIME, MANUAL].freeze
|
15
|
+
ASK = "Ask".freeze
|
16
|
+
BID = "Bid".freeze
|
15
17
|
|
16
|
-
def self.connect(options,
|
18
|
+
def self.connect(options, on_trade, on_quote)
|
17
19
|
EM.run do
|
18
|
-
client = ::Intrinio::Realtime::Client.new(options)
|
19
|
-
client.on_quote(&b)
|
20
|
+
client = ::Intrinio::Realtime::Client.new(options, on_trade, on_quote)
|
20
21
|
client.connect()
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
25
|
+
class Trade
|
26
|
+
def initialize(symbol, price, size, timestamp, total_volume)
|
27
|
+
@symbol = symbol
|
28
|
+
@price = price
|
29
|
+
@size = size
|
30
|
+
@timestamp = timestamp
|
31
|
+
@total_volume = total_volume
|
32
|
+
end
|
33
|
+
|
34
|
+
def symbol
|
35
|
+
@symbol
|
36
|
+
end
|
37
|
+
|
38
|
+
def price
|
39
|
+
@price
|
40
|
+
end
|
41
|
+
|
42
|
+
def size
|
43
|
+
@size
|
44
|
+
end
|
45
|
+
|
46
|
+
def timestamp
|
47
|
+
@timestamp
|
48
|
+
end
|
49
|
+
|
50
|
+
def total_volume
|
51
|
+
@total_volume
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
[@symbol, @price, @size, @timestamp, @total_volume].join(",")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Quote
|
60
|
+
def initialize(type, symbol, price, size, timestamp)
|
61
|
+
@type = type
|
62
|
+
@symbol = symbol
|
63
|
+
@price = price
|
64
|
+
@size = size
|
65
|
+
@timestamp = timestamp
|
66
|
+
end
|
67
|
+
|
68
|
+
def type
|
69
|
+
@type
|
70
|
+
end
|
71
|
+
|
72
|
+
def symbol
|
73
|
+
@symbol
|
74
|
+
end
|
75
|
+
|
76
|
+
def price
|
77
|
+
@price
|
78
|
+
end
|
79
|
+
|
80
|
+
def size
|
81
|
+
@size
|
82
|
+
end
|
83
|
+
|
84
|
+
def timestamp
|
85
|
+
@timestamp
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s
|
89
|
+
[@symbol, @type, @price, @size, @timestamp].join(",")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
24
93
|
class Client
|
25
94
|
|
26
|
-
def initialize(options)
|
95
|
+
def initialize(options, on_trade, on_quote)
|
27
96
|
raise "Options parameter is required" if options.nil? || !options.is_a?(Hash)
|
97
|
+
@stop = false
|
98
|
+
@messages = Queue.new
|
99
|
+
raise "Unable to create queue." if @messages.nil?
|
100
|
+
@on_trade = on_trade
|
101
|
+
@on_quote = on_quote
|
28
102
|
|
29
103
|
@api_key = options[:api_key]
|
30
104
|
raise "API Key was formatted invalidly." if @api_key && !valid_api_key?(@api_key)
|
31
|
-
|
105
|
+
|
32
106
|
unless @api_key
|
33
107
|
@username = options[:username]
|
34
108
|
@password = options[:password]
|
@@ -36,7 +110,25 @@ module Intrinio
|
|
36
110
|
end
|
37
111
|
|
38
112
|
@provider = options[:provider]
|
39
|
-
|
113
|
+
unless @provider
|
114
|
+
@provider = REALTIME
|
115
|
+
end
|
116
|
+
raise "Provider must be 'REALTIME' or 'MANUAL'" unless PROVIDERS.include?(@provider)
|
117
|
+
|
118
|
+
@ip_address = options[:ip_address]
|
119
|
+
raise "Missing option ip_address while in MANUAL mode." if @provider == MANUAL and (@ip_address.nil? || @ip_address.empty?)
|
120
|
+
|
121
|
+
@trades_only = options[:trades_only]
|
122
|
+
if @trades_only.nil?
|
123
|
+
@trades_only = false
|
124
|
+
end
|
125
|
+
|
126
|
+
@thread_quantity = options[:threads]
|
127
|
+
unless @thread_quantity
|
128
|
+
@thread_quantity = 4
|
129
|
+
end
|
130
|
+
|
131
|
+
@threads = []
|
40
132
|
|
41
133
|
@channels = []
|
42
134
|
@channels = parse_channels(options[:channels]) if options[:channels]
|
@@ -52,7 +144,6 @@ module Intrinio
|
|
52
144
|
@logger.level = Logger::INFO
|
53
145
|
end
|
54
146
|
|
55
|
-
@quotes = EventMachine::Channel.new
|
56
147
|
@ready = false
|
57
148
|
@joined_channels = []
|
58
149
|
@heartbeat_timer = nil
|
@@ -93,11 +184,7 @@ module Intrinio
|
|
93
184
|
debug "Leaving all channels"
|
94
185
|
refresh_channels()
|
95
186
|
end
|
96
|
-
|
97
|
-
def on_quote(&b)
|
98
|
-
@quotes.subscribe(&b)
|
99
|
-
end
|
100
|
-
|
187
|
+
|
101
188
|
def connect
|
102
189
|
raise "Must be run from within an EventMachine run loop" unless EM.reactor_running?
|
103
190
|
return warn("Already connected!") if @ready
|
@@ -124,22 +211,131 @@ module Intrinio
|
|
124
211
|
@channels = []
|
125
212
|
@joined_channels = []
|
126
213
|
@ws.close() if @ws
|
214
|
+
@stop = true
|
215
|
+
sleep(2)
|
216
|
+
@threads.each { |thread|
|
217
|
+
if !thread.nil? && (!thread.pending_interrupt? || thread.status == "run" || thread.status == "Sleeping")
|
218
|
+
then thread.join(7)
|
219
|
+
elsif !thread.nil?
|
220
|
+
then thread.kill
|
221
|
+
end
|
222
|
+
}
|
223
|
+
@threads = []
|
224
|
+
@stop = false
|
127
225
|
info "Connection closed"
|
128
226
|
end
|
227
|
+
|
228
|
+
def on_trade(on_trade)
|
229
|
+
@on_trade = on_trade
|
230
|
+
end
|
231
|
+
|
232
|
+
def on_quote(on_quote)
|
233
|
+
@on_quote = on_quote
|
234
|
+
end
|
129
235
|
|
130
236
|
private
|
131
|
-
|
237
|
+
|
238
|
+
def queue_message(message)
|
239
|
+
@messages.enq(message)
|
240
|
+
end
|
241
|
+
|
242
|
+
def parse_uint64(data)
|
243
|
+
data.map { |i| [sprintf('%02x',i)].pack('H2') }.join.unpack('Q<').first
|
244
|
+
end
|
245
|
+
|
246
|
+
def parse_int32(data)
|
247
|
+
data.map { |i| [sprintf('%02x',i)].pack('H2') }.join.unpack('l<').first
|
248
|
+
end
|
249
|
+
|
250
|
+
def parse_uint32(data)
|
251
|
+
data.map { |i| [sprintf('%02x',i)].pack('H2') }.join.unpack('V').first
|
252
|
+
end
|
253
|
+
|
254
|
+
def parse_trade(data, start_index, symbol_length)
|
255
|
+
symbol = data[start_index + 2, symbol_length].map!{|c| c.chr}.join
|
256
|
+
price = parse_int32(data[start_index + 2 + symbol_length, 4]).to_f / 10000.0
|
257
|
+
size = parse_uint32(data[start_index + 6 + symbol_length, 4])
|
258
|
+
timestamp = parse_uint64(data[start_index + 10 + symbol_length, 8])
|
259
|
+
total_volume = parse_uint32(data[start_index + 18 + symbol_length, 4])
|
260
|
+
return Trade.new(symbol, price, size, timestamp, total_volume)
|
261
|
+
end
|
262
|
+
|
263
|
+
def parse_quote(data, start_index, symbol_length, msg_type)
|
264
|
+
type = case when msg_type == 1 then ASK when msg_type == 2 then BID end
|
265
|
+
symbol = data[start_index + 2, symbol_length].map!{|c| c.chr}.join
|
266
|
+
price = parse_int32(data[start_index + 2 + symbol_length, 4]).to_f / 10000.0
|
267
|
+
size = parse_uint32(data[start_index + 6 + symbol_length, 4])
|
268
|
+
timestamp = parse_uint64(data[start_index + 10 + symbol_length, 8])
|
269
|
+
return Quote.new(type, symbol, price, size, timestamp)
|
270
|
+
end
|
271
|
+
|
272
|
+
def handle_message(data, start_index)
|
273
|
+
msg_type = data[start_index]
|
274
|
+
symbol_length = data[start_index + 1]
|
275
|
+
case msg_type
|
276
|
+
when 0 then
|
277
|
+
trade = parse_trade(data, start_index, symbol_length)
|
278
|
+
@on_trade.call(trade)
|
279
|
+
return start_index + 22 + symbol_length
|
280
|
+
when 1 || 2 then
|
281
|
+
quote = parse_quote(data, start_index, symbol_length, msg_type)
|
282
|
+
@on_quote.call(quote)
|
283
|
+
return start_index + 18 + symbol_length
|
284
|
+
end
|
285
|
+
return start_index
|
286
|
+
end
|
287
|
+
|
288
|
+
def handle_data
|
289
|
+
Thread.current.priority -= 1
|
290
|
+
me = self
|
291
|
+
pop = nil
|
292
|
+
until @stop do
|
293
|
+
begin
|
294
|
+
pop = nil
|
295
|
+
data = nil
|
296
|
+
pop = @messages.deq
|
297
|
+
unless pop.nil?
|
298
|
+
begin
|
299
|
+
data = pop.unpack('C*')
|
300
|
+
rescue StandardError => ex
|
301
|
+
me.send :error, "Error unpacking data from queue: #{ex} #{pop}"
|
302
|
+
next
|
303
|
+
end
|
304
|
+
if !data then me.send :error, "Cannot process data. Data is nil. #{pop}" end
|
305
|
+
start_index = 1
|
306
|
+
count = data[0]
|
307
|
+
# These are grouped (many) messages.
|
308
|
+
# The first byte tells us how many there are.
|
309
|
+
# From there, check the type and symbol length at index 0 of each chunk to know how many bytes each message has.
|
310
|
+
count.times {start_index = handle_message(data, start_index)}
|
311
|
+
end
|
312
|
+
if pop.nil? then sleep(0.1) end
|
313
|
+
rescue StandardError => e
|
314
|
+
me.send :error, "Error handling message from queue: #{e} #{pop} : #{data} ; count: #{count} ; start index: #{start_index}"
|
315
|
+
rescue Exception => e
|
316
|
+
#me.send :error, "General error handling message from queue: #{e} #{pop} : #{data} ; count: #{count} ; start index: #{start_index}"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
132
321
|
def refresh_token
|
133
322
|
@token = nil
|
134
323
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
324
|
+
uri = URI.parse(auth_url)
|
325
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
326
|
+
http.use_ssl = true if (auth_url.include?("https"))
|
327
|
+
http.start
|
328
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
329
|
+
request.add_field("Client-Information", "IntrinioRealtimeRubySDKv3.1")
|
330
|
+
|
331
|
+
unless @api_key
|
332
|
+
request.basic_auth(@username, @password)
|
139
333
|
end
|
140
334
|
|
141
|
-
|
142
|
-
|
335
|
+
response = http.request(request)
|
336
|
+
|
337
|
+
return fatal("Unable to authorize") if response.code == "401"
|
338
|
+
return fatal("Could not get auth token") if response.code != "200"
|
143
339
|
|
144
340
|
@token = response.body
|
145
341
|
debug "Token refreshed"
|
@@ -149,9 +345,8 @@ module Intrinio
|
|
149
345
|
url = ""
|
150
346
|
|
151
347
|
case @provider
|
152
|
-
when
|
153
|
-
|
154
|
-
when CRYPTOQUOTE then url = "https://crypto.intrinio.com/auth"
|
348
|
+
when REALTIME then url = "https://realtime-mx.intrinio.com/auth"
|
349
|
+
when MANUAL then url = "http://" + @ip_address + "/auth"
|
155
350
|
end
|
156
351
|
|
157
352
|
url = api_auth_url(url) if @api_key
|
@@ -160,7 +355,7 @@ module Intrinio
|
|
160
355
|
end
|
161
356
|
|
162
357
|
def api_auth_url(url)
|
163
|
-
if
|
358
|
+
if url.include? "?"
|
164
359
|
url = "#{url}&"
|
165
360
|
else
|
166
361
|
url = "#{url}?"
|
@@ -170,10 +365,9 @@ module Intrinio
|
|
170
365
|
end
|
171
366
|
|
172
367
|
def socket_url
|
173
|
-
case @provider
|
174
|
-
|
175
|
-
|
176
|
-
when CRYPTOQUOTE then URI.escape("wss://crypto.intrinio.com/socket/websocket?vsn=1.0.0&token=#{@token}")
|
368
|
+
case @provider
|
369
|
+
when REALTIME then URI.escape("wss://realtime-mx.intrinio.com/socket/websocket?vsn=1.0.0&token=#{@token}")
|
370
|
+
when MANUAL then URI.escape("ws://" + @ip_address + "/socket/websocket?vsn=1.0.0&token=#{@token}")
|
177
371
|
end
|
178
372
|
end
|
179
373
|
|
@@ -183,6 +377,19 @@ module Intrinio
|
|
183
377
|
@ws.close() unless @ws.nil?
|
184
378
|
@ready = false
|
185
379
|
@joined_channels = []
|
380
|
+
|
381
|
+
@stop = true
|
382
|
+
sleep(2)
|
383
|
+
@threads.each { |thread|
|
384
|
+
if !thread.nil? && (!thread.pending_interrupt? || thread.status == "run" || thread.status == "Sleeping")
|
385
|
+
then thread.join(7)
|
386
|
+
elsif !thread.nil?
|
387
|
+
then thread.kill
|
388
|
+
end
|
389
|
+
}
|
390
|
+
@threads = []
|
391
|
+
@stop = false
|
392
|
+
@thread_quantity.times {@threads << Thread.new{handle_data}}
|
186
393
|
|
187
394
|
@ws = ws = WebSocket::Client::Simple.connect(socket_url)
|
188
395
|
me.send :info, "Connection opening"
|
@@ -190,7 +397,7 @@ module Intrinio
|
|
190
397
|
ws.on :open do
|
191
398
|
me.send :info, "Connection established"
|
192
399
|
me.send :ready, true
|
193
|
-
if
|
400
|
+
if [REALTIME, MANUAL].include?(me.send(:provider))
|
194
401
|
me.send :refresh_channels
|
195
402
|
end
|
196
403
|
me.send :start_heartbeat
|
@@ -198,39 +405,21 @@ module Intrinio
|
|
198
405
|
end
|
199
406
|
|
200
407
|
ws.on :message do |frame|
|
201
|
-
|
202
|
-
me.send :debug, "Message: #{
|
203
|
-
|
408
|
+
data_message = frame.data
|
409
|
+
#me.send :debug, "Message: #{data_message}"
|
204
410
|
begin
|
205
|
-
|
206
|
-
|
207
|
-
quote = case me.send(:provider)
|
208
|
-
when IEX
|
209
|
-
if json["event"] == "quote"
|
210
|
-
json["payload"]
|
211
|
-
end
|
212
|
-
when QUODD
|
213
|
-
if json["event"] == "info" && json["data"]["message"] == "Connected"
|
214
|
-
me.send :refresh_channels
|
215
|
-
elsif json["event"] == "quote" || json["event"] == "trade"
|
216
|
-
json["data"]
|
217
|
-
end
|
218
|
-
when CRYPTOQUOTE
|
219
|
-
if json["event"] == "book_update" || json["event"] == "ticker" || json["event"] == "trade"
|
220
|
-
json["payload"]
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
if quote && quote.is_a?(Hash)
|
225
|
-
me.send :process_quote, quote
|
411
|
+
unless data_message.nil?
|
412
|
+
then me.send :queue_message, data_message
|
226
413
|
end
|
227
414
|
rescue StandardError => e
|
228
|
-
me.send :error, "
|
415
|
+
me.send :error, "Error adding message to queue: #{data_message} #{e}"
|
229
416
|
end
|
230
417
|
end
|
231
418
|
|
232
419
|
ws.on :close do |e|
|
233
|
-
me.send :
|
420
|
+
me.send :ready, false
|
421
|
+
me.send :info, "Connection closing...: #{e}"
|
422
|
+
me.send :try_self_heal
|
234
423
|
end
|
235
424
|
|
236
425
|
ws.on :error do |e|
|
@@ -247,16 +436,20 @@ module Intrinio
|
|
247
436
|
# Join new channels
|
248
437
|
new_channels = @channels - @joined_channels
|
249
438
|
new_channels.each do |channel|
|
250
|
-
msg = join_message(channel)
|
251
|
-
|
439
|
+
#msg = join_message(channel)
|
440
|
+
#@ws.send(msg.to_json)
|
441
|
+
msg = join_binary_message(channel)
|
442
|
+
@ws.send(msg)
|
252
443
|
info "Joined #{channel}"
|
253
444
|
end
|
254
445
|
|
255
446
|
# Leave old channels
|
256
447
|
old_channels = @joined_channels - @channels
|
257
448
|
old_channels.each do |channel|
|
258
|
-
msg =
|
259
|
-
|
449
|
+
#msg = leave__message(channel)
|
450
|
+
#@ws.send(msg.to_json)
|
451
|
+
msg = leave_binary_message(channel)
|
452
|
+
@ws.send(msg)
|
260
453
|
info "Left #{channel}"
|
261
454
|
end
|
262
455
|
|
@@ -276,11 +469,7 @@ module Intrinio
|
|
276
469
|
end
|
277
470
|
|
278
471
|
def heartbeat_msg
|
279
|
-
|
280
|
-
when IEX then {topic: 'phoenix', event: 'heartbeat', payload: {}, ref: nil}.to_json
|
281
|
-
when QUODD then {event: 'heartbeat', data: {action: 'heartbeat', ticker: (Time.now.to_f * 1000).to_i}}.to_json
|
282
|
-
when CRYPTOQUOTE then {topic: 'phoenix', event: 'heartbeat', payload: {}, ref: nil}.to_json
|
283
|
-
end
|
472
|
+
""
|
284
473
|
end
|
285
474
|
|
286
475
|
def stop_heartbeat
|
@@ -310,11 +499,7 @@ module Intrinio
|
|
310
499
|
def ready(val)
|
311
500
|
@ready = val
|
312
501
|
end
|
313
|
-
|
314
|
-
def process_quote(quote)
|
315
|
-
@quotes.push(quote)
|
316
|
-
end
|
317
|
-
|
502
|
+
|
318
503
|
def debug(message)
|
319
504
|
message = "IntrinioRealtime | #{message}"
|
320
505
|
@logger.debug(message) rescue
|
@@ -348,68 +533,26 @@ module Intrinio
|
|
348
533
|
channels
|
349
534
|
end
|
350
535
|
|
351
|
-
def
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
"iex:lobby:last_price"
|
536
|
+
def join_binary_message(channel)
|
537
|
+
if (channel == "lobby") && (@trades_only == false)
|
538
|
+
return [74, 0, 36, 70, 73, 82, 69, 72, 79, 83, 69].pack('C*') #74, not trades only, "$FIREHOSE"
|
539
|
+
elsif (channel == "lobby") && (@trades_only == true)
|
540
|
+
return [74, 1, 36, 70, 73, 82, 69, 72, 79, 83, 69].pack('C*') #74, trades only, "$FIREHOSE"
|
357
541
|
else
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
case @provider
|
364
|
-
when IEX
|
365
|
-
{
|
366
|
-
topic: parse_iex_topic(channel),
|
367
|
-
event: "phx_join",
|
368
|
-
payload: {},
|
369
|
-
ref: nil
|
370
|
-
}
|
371
|
-
when QUODD
|
372
|
-
{
|
373
|
-
event: "subscribe",
|
374
|
-
data: {
|
375
|
-
ticker: channel,
|
376
|
-
action: "subscribe"
|
377
|
-
}
|
378
|
-
}
|
379
|
-
when CRYPTOQUOTE
|
380
|
-
{
|
381
|
-
topic: channel,
|
382
|
-
event: "phx_join",
|
383
|
-
payload: {},
|
384
|
-
ref: nil
|
385
|
-
}
|
542
|
+
bytes = [74, 0]
|
543
|
+
if (@trades_only == true)
|
544
|
+
bytes[1] = 1
|
545
|
+
end
|
546
|
+
return bytes.concat(channel.bytes).pack('C*')
|
386
547
|
end
|
387
548
|
end
|
388
|
-
|
389
|
-
def
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
payload: {},
|
396
|
-
ref: nil
|
397
|
-
}
|
398
|
-
when QUODD
|
399
|
-
{
|
400
|
-
event: "unsubscribe",
|
401
|
-
data: {
|
402
|
-
ticker: channel,
|
403
|
-
action: "unsubscribe"
|
404
|
-
}
|
405
|
-
}
|
406
|
-
when CRYPTOQUOTE
|
407
|
-
{
|
408
|
-
topic: channel,
|
409
|
-
event: "phx_leave",
|
410
|
-
payload: {},
|
411
|
-
ref: nil
|
412
|
-
}
|
549
|
+
|
550
|
+
def leave_binary_message(channel)
|
551
|
+
if channel == "lobby"
|
552
|
+
return [76, 36, 70, 73, 82, 69, 72, 79, 83, 69].pack('C*') #74, not trades only, "$FIREHOSE"
|
553
|
+
else
|
554
|
+
bytes = [76]
|
555
|
+
return bytes.concat(channel.bytes).pack('C*')
|
413
556
|
end
|
414
557
|
end
|
415
558
|
|
metadata
CHANGED
@@ -1,60 +1,74 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: intrinio-realtime
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Intrinio
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: eventmachine
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: websocket-client-simple
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0.3'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0.3'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: thread
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.2.2
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
55
|
-
|
54
|
+
version: 0.2.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bigdecimal
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.4.0
|
69
|
+
description: Intrinio Ruby SDK for Real-Time Stock Prices
|
56
70
|
email:
|
57
|
-
-
|
71
|
+
- ssnyder@intrinio.com
|
58
72
|
executables: []
|
59
73
|
extensions: []
|
60
74
|
extra_rdoc_files: []
|
@@ -64,7 +78,7 @@ homepage: https://github.com/intrinio/intrinio-realtime-ruby-sdk
|
|
64
78
|
licenses:
|
65
79
|
- GPL-3.0
|
66
80
|
metadata: {}
|
67
|
-
post_install_message:
|
81
|
+
post_install_message:
|
68
82
|
rdoc_options: []
|
69
83
|
require_paths:
|
70
84
|
- lib
|
@@ -79,10 +93,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
79
93
|
- !ruby/object:Gem::Version
|
80
94
|
version: '0'
|
81
95
|
requirements: []
|
82
|
-
|
83
|
-
|
84
|
-
signing_key:
|
96
|
+
rubygems_version: 3.2.15
|
97
|
+
signing_key:
|
85
98
|
specification_version: 4
|
86
|
-
summary: Intrinio provides real-time stock
|
87
|
-
|
99
|
+
summary: Intrinio provides real-time stock prices from its Multi-Exchange feed, via
|
100
|
+
a two-way WebSocket connection.
|
88
101
|
test_files: []
|