rtcbx 0.0.5 → 0.0.6

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