rtcbx 0.0.5 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: af8d9453a917ec30e849bb9b0e6a7037c3d2bc33
4
- data.tar.gz: 411406c26a51af2ffed7cf66e8ee5a06528ba95f
2
+ SHA256:
3
+ metadata.gz: a37a1edb1f28968d8bf6bdb6a3300d47553f816239e8b4c665be09e0288f51e7
4
+ data.tar.gz: b0e002cfaaa5eabf6266b4ba98000b4306197528983c2b4c0243701cdaea3452
5
5
  SHA512:
6
- metadata.gz: c02cb6efa7c3dd9633d9d3f9d9c3150d48cf960d490075c8a79a3f242d93b1251acf9c95fb2a8815529185569e04430d5fa2442e2521ae8b5fb1ae5e44f0fac6
7
- data.tar.gz: 891a7615b596bdcd454ebdadf960b9478b5869ca79c35f2b11efbd3787fdd995480fada1b127df389ff86af20429c8062946c4abe916dd572d02d9a9326ff365
6
+ metadata.gz: 4088bfa8b66f2bbeb8ac463ad25331d298aa53ff51bdd6b576d26add9ba66c4bf26bdf6c8c65595f989c2434eb371474b0adad69814aed92a1997e3345f294a9
7
+ data.tar.gz: 32fd07f117c43cacd387cbe7305e9353ac52111c6d47b1dab4a455cb12af85bbcf3b3af7b3e10933d7dcae8ec8439a30c17d196a71d245def053602e5e0869ff
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # RTCBX
2
+ [![Gem Version](https://badge.fury.io/rb/rtcbx.svg)](https://badge.fury.io/rb/rtcbx)
2
3
 
3
- RTCBX uses the Coinbase (now GDAX) Exchange websocket feed to provide immediate access to
4
+ RTCBX uses the Coinbase Pro (formerly GDAX and Coinbase Exchange) Exchange websocket feed to provide immediate access to
4
5
  the current state of the exchange without repeatedly polling parts of the
5
6
  RESTful API. It can:
6
7
  * Keep a synchronized copy of the entire orderbook - `RTCBX::Orderbook`
@@ -37,7 +38,7 @@ RTCBX objects share a common interface:
37
38
  ```ruby
38
39
  #
39
40
  # :product_id
40
- # sets the currency (defaults to 'BTC-USD)
41
+ # sets the currency (defaults to 'BTC-USD')
41
42
  #
42
43
  # :start
43
44
  # run #start! at creation? (defaults to true)
@@ -57,7 +58,7 @@ rtcbx.reset! # Calls #stop! then calls #start!.
57
58
 
58
59
 
59
60
 
60
-
61
+ ### Orderbooks
61
62
  * Create a live updating Orderbook:
62
63
  ```ruby
63
64
  ob = RTCBX::Orderbook.new
@@ -144,6 +145,8 @@ ob.aggregate_asks(10)
144
145
  # Will perform the aggregation on each call. Avoid abusing this function since it may degrade performance.
145
146
  ob.aggregate(10)
146
147
  ```
148
+ ### Candles
149
+ ### Trader
147
150
 
148
151
  ## Contributing
149
152
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rake/testtask'
2
4
  require 'bundler'
3
5
  require_relative './lib/rtcbx/version.rb'
@@ -6,7 +8,7 @@ task :build do
6
8
  begin
7
9
  puts 'building gem...'
8
10
  `gem build rtcbx.gemspec`
9
- rescue
11
+ rescue StandardError
10
12
  puts 'build failed.'
11
13
  end
12
14
  end
@@ -15,7 +17,7 @@ task :install do
15
17
  begin
16
18
  puts 'installing gem...'
17
19
  `gem install --local rtcbx`
18
- rescue
20
+ rescue StandardError
19
21
  puts 'install failed.'
20
22
  end
21
23
  end
@@ -27,7 +29,7 @@ task :console do
27
29
  PRY.start
28
30
  end
29
31
 
30
- task default: %w(build install)
32
+ task default: %w[build install]
31
33
 
32
34
  Rake::TestTask.new do |t|
33
35
  t.libs << 'test'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'coinbase/exchange'
2
4
  require 'rtcbx/orderbook'
3
5
  require 'rtcbx/candles'
@@ -10,7 +12,7 @@ class RTCBX
10
12
  #
11
13
  PING_INTERVAL = 2
12
14
 
13
- # The GDAX product being tracked (eg. "BTC-USD")
15
+ # The Coinbase Pro product being tracked (eg. "BTC-USD")
14
16
  attr_reader :product_id
15
17
 
16
18
  # Boolean, whether the orderbook goes live on creation or not
@@ -27,7 +29,7 @@ class RTCBX
27
29
  # The Websocket object
28
30
  attr_reader :websocket
29
31
 
30
- # The GDAX Client object
32
+ # The Coinbase Pro Client object
31
33
  # You can use this if you need to make API calls
32
34
  attr_reader :client
33
35
 
@@ -35,7 +37,7 @@ class RTCBX
35
37
  # The +websocket_thread+ processes this queue
36
38
  attr_reader :queue
37
39
 
38
- # Epoch time indicating the last time we received a pong from GDAX in response
40
+ # Epoch time indicating the last time we received a pong from Coinbase Pro in response
39
41
  # to one of our pings
40
42
  attr_reader :last_pong
41
43
 
@@ -103,7 +105,7 @@ class RTCBX
103
105
  def setup_websocket_callback
104
106
  websocket.message do |message|
105
107
  queue.push(message)
106
- message_callbacks.each { |b| b.call(message) unless b.nil? }
108
+ message_callbacks.each { |b| b&.call(message) }
107
109
  end
108
110
  end
109
111
 
@@ -119,7 +121,7 @@ class RTCBX
119
121
  end
120
122
  end
121
123
 
122
- # Configures the websocket to periodically ping GDAX and confirm connection
124
+ # Configures the websocket to periodically ping Coinbase Pro and confirm connection
123
125
  def setup_ping_timer
124
126
  EM.add_periodic_timer(PING_INTERVAL) do
125
127
  websocket.ping do
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rtcbx/candles/candle'
2
4
 
3
5
  class RTCBX
4
6
  class Candles < RTCBX
5
-
6
7
  # A hash of buckets
7
8
  # Each key is an epoch which stores every +match+ message for that minute
8
9
  # (The epoch plus 60 seconds)
@@ -10,7 +11,6 @@ class RTCBX
10
11
  # +Candle+
11
12
  attr_reader :buckets
12
13
 
13
-
14
14
  # This thread monitors the websocket object and puts each +match+ object
15
15
  # into the proper bucket. This thread maintains the +buckets+ object.
16
16
  attr_reader :bucket_thread
@@ -38,7 +38,6 @@ class RTCBX
38
38
  # Mutex to allow our two threads to produce and consume +buckets+
39
39
  attr_reader :buckets_lock
40
40
 
41
-
42
41
  # Create a new +Candles+ object to start and track candles
43
42
  # Pass a block to run a block whenever a candle is created.
44
43
  #
@@ -72,19 +71,19 @@ class RTCBX
72
71
 
73
72
  loop do
74
73
  message = queue.pop
75
- if message.fetch('type') == 'match'
76
- if Time.parse(message.fetch('time')) >= Time.at(first_bucket)
77
- timestamp = Time.parse(message.fetch('time'))
78
- message_bucket = timestamp.to_i - timestamp.sec
79
- @buckets_lock.synchronize do
80
- if message_bucket >= current_bucket
81
- @current_bucket = message_bucket
82
- @buckets[current_bucket.to_i] = []
83
- @buckets[current_bucket.to_i] << message
84
- else
85
- @buckets[current_bucket.to_i] << message
86
- end
87
- end
74
+ next unless message.fetch('type') == 'match'
75
+
76
+ next unless Time.parse(message.fetch('time')) >= Time.at(first_bucket)
77
+
78
+ timestamp = Time.parse(message.fetch('time'))
79
+ message_bucket = timestamp.to_i - timestamp.sec
80
+ @buckets_lock.synchronize do
81
+ if message_bucket >= current_bucket
82
+ @current_bucket = message_bucket
83
+ @buckets[current_bucket.to_i] = []
84
+ @buckets[current_bucket.to_i] << message
85
+ else
86
+ @buckets[current_bucket.to_i] << message
88
87
  end
89
88
  end
90
89
  end
@@ -98,15 +97,15 @@ class RTCBX
98
97
  sleep(60 - Time.now.sec)
99
98
  loop do
100
99
  buckets.keys.each do |key|
101
- if key + 60 <= Time.now.to_i
102
- @buckets_lock.synchronize do
103
- candle = Candle.new(key, buckets[key]) unless buckets[key].empty?
104
- @candles << candle
105
- # Run candle callback
106
- #
107
- @message_callbacks.each{|c| c.call(candle)}
108
- buckets.delete(key)
109
- end
100
+ next unless key + 60 <= Time.now.to_i
101
+
102
+ @buckets_lock.synchronize do
103
+ candle = Candle.new(key, buckets[key]) unless buckets[key].empty?
104
+ @candles << candle
105
+ # Run candle callback
106
+ #
107
+ @message_callbacks.each { |c| c.call(candle) }
108
+ buckets.delete(key)
110
109
  end
111
110
  end
112
111
 
@@ -1,7 +1,8 @@
1
- class RTCBX
1
+ # frozen_string_literal: true
2
+
3
+ class RTCBX
2
4
  class Candles < RTCBX
3
5
  class Candle
4
-
5
6
  # Candle values, this is standard
6
7
  attr_reader :time, :low, :high, :open, :close, :volume
7
8
 
@@ -9,22 +10,22 @@ class RTCBX
9
10
  # the interval of the candle
10
11
  def initialize(epoch, matches)
11
12
  @time = Time.at(epoch)
12
- @low = matches.map {|message| BigDecimal.new(message.fetch('price'))}.min
13
- @high = matches.map {|message| BigDecimal.new(message.fetch('price'))}.max
14
- @open = BigDecimal.new(matches.first.fetch('price'))
15
- @close = BigDecimal.new(matches.last.fetch('price'))
16
- @volume = matches.reduce(BigDecimal(0)) {|sum, message| sum + BigDecimal.new(message.fetch('size'))}
13
+ @low = matches.map { |message| BigDecimal(message.fetch('price')) }.min
14
+ @high = matches.map { |message| BigDecimal(message.fetch('price')) }.max
15
+ @open = BigDecimal(matches.first.fetch('price'))
16
+ @close = BigDecimal(matches.last.fetch('price'))
17
+ @volume = matches.reduce(BigDecimal(0)) { |sum, message| sum + BigDecimal(message.fetch('size')) }
17
18
  end
18
19
 
19
20
  # Return a +Hash+ representation of the +Candle+
20
21
  def to_h
21
22
  {
22
- start: Time.at(@time),
23
- low: @low.to_s("F"),
24
- high: @high.to_s("F"),
25
- open: @open.to_s("F"),
26
- close: @close.to_s("F"),
27
- volume: @volume.to_s("F"),
23
+ start: Time.at(@time),
24
+ low: @low.to_s('F'),
25
+ high: @high.to_s('F'),
26
+ open: @open.to_s('F'),
27
+ close: @close.to_s('F'),
28
+ volume: @volume.to_s('F')
28
29
  }
29
30
  end
30
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rtcbx/orderbook/book_methods'
2
4
  require 'rtcbx/orderbook/book_analysis'
3
5
 
@@ -34,7 +36,7 @@ class RTCBX
34
36
  #
35
37
  # If a +block+ is given it is passed each message as it is received.
36
38
  #
37
- def initialize(options={}, &block)
39
+ def initialize(options = {}, &block)
38
40
  @bids = []
39
41
  @asks = []
40
42
  @snapshot_sequence = 0
@@ -64,10 +66,9 @@ class RTCBX
64
66
  # Converts an order array from the API into a hash.
65
67
  #
66
68
  def order_to_hash(price, size, order_id)
67
- { price: BigDecimal.new(price),
68
- size: BigDecimal.new(size),
69
- order_id: order_id
70
- }
69
+ { price: BigDecimal(price),
70
+ size: BigDecimal(size),
71
+ order_id: order_id }
71
72
  end
72
73
 
73
74
  # Fetch orderbook snapshot from API and convert order arrays to hashes.
@@ -81,7 +82,7 @@ class RTCBX
81
82
  end
82
83
  end
83
84
 
84
- # Private method to actually start the thread that reads from the que and
85
+ # Private method to actually start the thread that reads from the queue and
85
86
  # updates the Orderbook state
86
87
  def start_update_thread
87
88
  @update_thread = Thread.new do
@@ -90,8 +91,7 @@ class RTCBX
90
91
  message = queue.pop
91
92
  apply(message)
92
93
  end
93
-
94
- rescue => e
94
+ rescue StandardError => e
95
95
  puts e
96
96
  end
97
97
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class RTCBX
2
4
  class Orderbook < RTCBX
3
5
  # Simple collection of commands to get info about the orderbook. Add our own
@@ -53,12 +55,12 @@ class RTCBX
53
55
 
54
56
  # The price of the best current bid
55
57
  def best_bid
56
- @bids.sort_by { |x| x.fetch(:price) }.last
58
+ @bids.max_by { |x| x.fetch(:price) }
57
59
  end
58
60
 
59
61
  # The price of the best current ask
60
62
  def best_ask
61
- @asks.sort_by { |x| x.fetch(:price) }.first
63
+ @asks.min_by { |x| x.fetch(:price) }
62
64
  end
63
65
 
64
66
  # The prices of the best current bid and ask
@@ -72,7 +74,7 @@ class RTCBX
72
74
  end
73
75
 
74
76
  # Aggregates the +top_n+ current bids. Pass `50` and you'll get the same
75
- # thing tht GDAX calls a "Level 2 Orderbook"
77
+ # thing tht Coinbase Pro calls a "Level 2 Orderbook"
76
78
  def aggregate_bids(top_n = nil)
77
79
  aggregate = {}
78
80
  @bids.each do |bid|
@@ -84,13 +86,12 @@ class RTCBX
84
86
  aggregate.keys.sort.reverse.first(top_n).map do |price|
85
87
  { price: price,
86
88
  size: aggregate[price][:size],
87
- num_orders: aggregate[price][:num_orders]
88
- }
89
+ num_orders: aggregate[price][:num_orders] }
89
90
  end
90
91
  end
91
92
 
92
93
  # Aggregates the +top_n+ current asks. Pass `50` and you'll get the same
93
- # thing tht GDAX calls a "Level 2 Orderbook"
94
+ # thing tht Coinbase Pro calls a "Level 2 Orderbook"
94
95
  def aggregate_asks(top_n = nil)
95
96
  aggregate = {}
96
97
  @asks.each do |ask|
@@ -102,13 +103,12 @@ class RTCBX
102
103
  aggregate.keys.sort.first(top_n).map do |price|
103
104
  { price: price,
104
105
  size: aggregate[price][:size],
105
- num_orders: aggregate[price][:num_orders]
106
- }
106
+ num_orders: aggregate[price][:num_orders] }
107
107
  end
108
108
  end
109
109
 
110
110
  # Aggregates the +top_n+ current asks and bids. Pass `50` and you'll get the same
111
- # thing tht GDAX calls a "Level 2 Orderbook"
111
+ # thing tht Coinbase Pro calls a "Level 2 Orderbook"
112
112
  def aggregate(top_n = nil)
113
113
  { bids: aggregate_bids(top_n), asks: aggregate_asks(top_n) }
114
114
  end
@@ -122,7 +122,7 @@ class RTCBX
122
122
  private
123
123
 
124
124
  def aggregate_base
125
- { size: BigDecimal.new(0), num_orders: 0 }
125
+ { size: BigDecimal(0), num_orders: 0 }
126
126
  end
127
127
  end
128
128
  end
@@ -1,28 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bigdecimal'
2
4
  class RTCBX
3
5
  class Orderbook < RTCBX
4
-
5
6
  # This class provides methods to apply updates to the state of the orderbook
6
7
  # as they are received by the websocket.
7
8
  #
8
9
  module BookMethods
9
-
10
10
  # Names of attributes that should be converted to +BigDecimal+
11
- BIGDECIMAL_KEYS = %w(size old_size new_size remaining_size price)
11
+ BIGDECIMAL_KEYS = %w[size old_size new_size remaining_size price].freeze
12
12
 
13
13
  # Applies a message to an Orderbook object by making relevant changes to
14
14
  # @bids, @asks, and @last_sequence.
15
15
  #
16
16
  def apply(msg)
17
17
  return if msg.fetch('sequence') != @last_sequence + 1
18
- #if msg.fetch('sequence') != @last_sequence + 1
18
+
19
+ # if msg.fetch('sequence') != @last_sequence + 1
19
20
  # puts "Expected #{@last_sequence + 1}, got #{msg.fetch('sequence')}"
20
21
  # @websocket.stop!
21
- #end
22
+ # end
22
23
 
23
24
  @last_sequence = msg.fetch('sequence')
24
25
  BIGDECIMAL_KEYS.each do |key|
25
- msg[key] = BigDecimal.new(msg.fetch(key)) if msg.fetch(key, false)
26
+ msg[key] = BigDecimal(msg.fetch(key)) if msg.fetch(key, false)
26
27
  end
27
28
 
28
29
  __send__(msg.fetch('type'), msg)
@@ -32,8 +33,8 @@ class RTCBX
32
33
 
33
34
  def open(msg)
34
35
  order = {
35
- price: msg.fetch('price'),
36
- size: msg.fetch('remaining_size'),
36
+ price: msg.fetch('price'),
37
+ size: msg.fetch('remaining_size'),
37
38
  order_id: msg.fetch('order_id')
38
39
  }
39
40
 
@@ -1,5 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class RTCBX
2
4
  class Trader < RTCBX
3
-
4
5
  end
5
6
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Orderbook version number. I try to keep it semantic.
2
4
  #
3
5
  class RTCBX
4
- VERSION = '0.0.5'
6
+ VERSION = '0.0.6'
5
7
  end
@@ -1,5 +1,6 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'rtcbx/version'
5
6
 
@@ -8,10 +9,11 @@ Gem::Specification.new do |spec|
8
9
  spec.version = RTCBX::VERSION
9
10
  spec.authors = ['Michael Rodrigues']
10
11
  spec.email = ['mikebrodrigues@gmail.com']
11
- spec.summary = %q(Maintains an real-time copy of the GDAX (Coinbase) Exchange order book.)
12
- spec.description = %q(Uses the GDAX (Coinbase) Exchange Websocket stream
12
+ spec.summary = 'Maintains an real-time copy of the Coinbase Pro (formerly GDAX,
13
+ formerly Coinbase Exchange) Exchange order book.'
14
+ spec.description = 'Uses the Coinbase Pro (formerly GDAX, formerly Coinbase Exchange) Exchange Websocket stream
13
15
  to maintain a real-time copy of the order book, place and track orders,
14
- and calculate historic rates by the minute in real-time.)
16
+ and calculate historic rates by the minute in real-time.'
15
17
  spec.homepage = 'https://github.com/mikerodrigues/rtcbx'
16
18
  spec.license = 'MIT'
17
19
 
@@ -23,7 +25,7 @@ Gem::Specification.new do |spec|
23
25
  spec.add_runtime_dependency 'coinbase-exchange', '~> 0.2', '>= 0.2.0'
24
26
  spec.add_runtime_dependency 'eventmachine', '~> 1.0'
25
27
  spec.add_runtime_dependency 'json', '~> 2.0'
26
-
28
+
27
29
  spec.add_development_dependency 'bundler', '~> 1.7'
28
- spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rake', '~> 12.3'
29
31
  end
metadata CHANGED
@@ -1,35 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtcbx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Rodrigues
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-10 00:00:00.000000000 Z
11
+ date: 2020-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coinbase-exchange
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.2'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
19
  version: 0.2.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '0.2'
30
27
  - - ">="
31
28
  - !ruby/object:Gem::Version
32
29
  version: 0.2.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.2'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: eventmachine
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -78,16 +78,16 @@ dependencies:
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '10.0'
81
+ version: '12.3'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '10.0'
88
+ version: '12.3'
89
89
  description: |-
90
- Uses the GDAX (Coinbase) Exchange Websocket stream
90
+ Uses the Coinbase Pro (formerly GDAX, formerly Coinbase Exchange) Exchange Websocket stream
91
91
  to maintain a real-time copy of the order book, place and track orders,
92
92
  and calculate historic rates by the minute in real-time.
93
93
  email:
@@ -128,10 +128,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
128
  - !ruby/object:Gem::Version
129
129
  version: '0'
130
130
  requirements: []
131
- rubyforge_project:
132
- rubygems_version: 2.4.6
131
+ rubygems_version: 3.0.3
133
132
  signing_key:
134
133
  specification_version: 4
135
- summary: Maintains an real-time copy of the GDAX (Coinbase) Exchange order book.
134
+ summary: Maintains an real-time copy of the Coinbase Pro (formerly GDAX, formerly
135
+ Coinbase Exchange) Exchange order book.
136
136
  test_files: []
137
- has_rdoc: