orderbook 2.0.2 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 82bf3042bbd14421f52d056b6bd7c07ded3a4745
4
- data.tar.gz: 04b949de80478883f3400d7d90ba99ca61160e15
3
+ metadata.gz: 20a70a3b134b39779181f6f4137a5200f003593e
4
+ data.tar.gz: 269d90ef37d27f7925e10f241326cf65c41ad3e1
5
5
  SHA512:
6
- metadata.gz: 701b49e1646e44a5e08c7e3f09ea480f90bd7c47d629aa1be1c1c31f11de0137c893619c177e9491e2105b0e7c1668507090d2f836dc268b62825501a02fa784
7
- data.tar.gz: df12ea165dffa99585bc74eb50d600c76065f4b58a7fcfb617fd873b6923aab0340000a85d7eed79e0757b53afc00991852e7002ec8dfe29a47ad0da1f492ad5
6
+ metadata.gz: 255c3b44bb6cb4bee6bd90975bac52f51401d79d0e4374250545e438f2348223ca01e78a348f23bffa1e444d1d0aa479b024872fcdd40b3ec3ff17e415d5e426
7
+ data.tar.gz: a1581ff672f3e345c5745171754a399f6b552203fa73828976468445d36883a79f72a05d2eaddbe67176c1fc57dd33ff21dea919ec16ab71f649c9944c2f1444
data/Gemfile CHANGED
@@ -4,3 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
  gem 'coinbase-exchange', git: 'https://github.com/mikerodrigues/coinbase-exchange-ruby.git', branch: 'orderbook'
6
6
  gem 'json'
7
+ gem 'eventmachine'
@@ -1,6 +1,6 @@
1
1
  GIT
2
2
  remote: https://github.com/mikerodrigues/coinbase-exchange-ruby.git
3
- revision: 051b256d2d0e414d7aec5bdb9a69ad33bfcf7e20
3
+ revision: 527d6b4e9d5e6bd2d2deab7ac5f16f58674194bc
4
4
  branch: orderbook
5
5
  specs:
6
6
  coinbase-exchange (0.1.2)
@@ -11,7 +11,7 @@ GIT
11
11
  PATH
12
12
  remote: .
13
13
  specs:
14
- orderbook (2.0.1)
14
+ orderbook (3.0.0)
15
15
 
16
16
  GEM
17
17
  remote: https://rubygems.org/
@@ -27,7 +27,7 @@ GEM
27
27
  http_parser.rb (>= 0.6.0)
28
28
  em-socksify (0.3.0)
29
29
  eventmachine (>= 1.0.0.beta.4)
30
- eventmachine (1.0.7)
30
+ eventmachine (1.0.8)
31
31
  faye-websocket (0.10.0)
32
32
  eventmachine (>= 0.12.0)
33
33
  websocket-driver (>= 0.5.1)
@@ -44,6 +44,7 @@ PLATFORMS
44
44
  DEPENDENCIES
45
45
  bundler (~> 1.7)
46
46
  coinbase-exchange!
47
+ eventmachine
47
48
  json
48
49
  orderbook!
49
50
  rake (~> 10.0)
data/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # Orderbook
1
+ # Orderbook 3.0.0
2
+ <a href="https://codeclimate.com/github/mikerodrigues/orderbook"><img src="https://codeclimate.com/github/mikerodrigues/orderbook/badges/gpa.svg" /></a>
3
+
4
+ Version 3.0.0 has a slightly different interface and properly queues messages
5
+ for an accurate Orderbook.
2
6
 
3
7
  A gem for creating a realtime order book for the Coinbase Exchange.
4
8
 
@@ -43,7 +47,7 @@ ob = Orderbook.new(false)
43
47
 
44
48
  # When you want it to go live:
45
49
 
46
- ob.live!
50
+ ob.start!
47
51
  ```
48
52
 
49
53
  * Create a live Orderbook with a callback to fire on each message:
@@ -53,18 +57,13 @@ ob = Orderbook.new do |message|
53
57
  end
54
58
  ```
55
59
 
56
- * Create or reset the callback:
60
+ * Create or reset the message callback:
57
61
  ```ruby
58
- ob.callback = lambda do |message|
59
- puts message.fetch 'callback'
62
+ ob.on_message do |message|
63
+ puts message.fetch 'sequence'
60
64
  end
61
65
  ```
62
66
 
63
- * The old class name is still supported and is equivalent to an Orderbook:
64
- ```ruby
65
- rtb = Orderbook::RealTimeBook.new
66
- ```
67
-
68
67
  * List current bids:
69
68
  ```ruby
70
69
  ob.bids
@@ -77,7 +76,7 @@ ob.asks
77
76
 
78
77
  * Show sequence number for initial level 3 snapshot:
79
78
  ```ruby
80
- ob.first_sequence
79
+ ob.snapshot_sequence
81
80
  ```
82
81
 
83
82
  * Show sequence number for the last message received
@@ -11,7 +11,9 @@ class Orderbook
11
11
  include BookMethods
12
12
  include BookAnalysis
13
13
 
14
- PING_INTERVAL = 15 # seconds in between pinging the connection.
14
+ # seconds in between pinging the connection.
15
+ #
16
+ PING_INTERVAL = 15
15
17
 
16
18
  # Array of bids
17
19
  #
@@ -23,7 +25,7 @@ class Orderbook
23
25
 
24
26
  # Sequence number from the initial level 3 snapshot
25
27
  #
26
- attr_reader :first_sequence
28
+ attr_reader :snapshot_sequence
27
29
 
28
30
  # Sequence number of most recently received message
29
31
  #
@@ -33,71 +35,107 @@ class Orderbook
33
35
  #
34
36
  attr_reader :websocket
35
37
 
36
- # Coinbase::Exchange::AsyncClient object
38
+ # Coinbase::Exchange::Client object
37
39
  #
38
40
  attr_reader :client
39
41
 
40
- # Thread running the EM loop
42
+ # Thread running the EM loop for the websocket
43
+ #
44
+ attr_reader :em_thread
45
+
46
+ # Thread running the processing loop
41
47
  #
42
- attr_reader :thread
48
+ attr_reader :processing_thread
43
49
 
44
- # Last time a pong was received after a ping
50
+ # DateTime of last successful pong
45
51
  #
46
52
  attr_reader :last_pong
47
53
 
48
- # Callback to pass each received message to
54
+ # Message queue for incoming messages.
49
55
  #
50
- attr_accessor :callback
56
+ attr_reader :queue
51
57
 
52
58
  # Creates a new live copy of the orderbook.
53
59
  #
54
- # If +live+ is set to false, the orderbook will not start automatically.
60
+ # If +start+ is set to false, the orderbook will not start automatically.
55
61
  #
56
62
  # If a +block+ is given it is passed each message as it is received.
57
63
  #
58
- def initialize(live = true, &block)
59
- @bids = [{ price: nil, size: nil, order_id: nil }]
60
- @asks = [{ price: nil, size: nil, order_id: nil }]
61
- @first_sequence = 0
64
+ def initialize(start = true, &block)
65
+ @bids = []
66
+ @asks = []
67
+ @snapshot_sequence = 0
62
68
  @last_sequence = 0
69
+ @queue = Queue.new
63
70
  @websocket = Coinbase::Exchange::Websocket.new(keepalive: true)
64
- @client = Coinbase::Exchange::AsyncClient.new
65
- @callback = block if block_given?
66
- live && live!
71
+ @client = Coinbase::Exchange::Client.new
72
+ @on_message = block if block_given?
73
+ start && start!
67
74
  end
68
75
 
69
76
  # Used to start the thread that listens to updates on the websocket and
70
77
  # applies them to the current orderbook to create a live book.
71
78
  #
72
- def live!
73
- setup_websocket
74
- start_thread
79
+ def start!
80
+ start_em_thread
81
+
82
+ # Wait to make sure the snapshot sequence ID is higher than the sequence of
83
+ # the first message in the queue.
84
+ #
85
+ sleep 0.3
86
+ apply_orderbook_snapshot
87
+ start_processing_thread
88
+ end
89
+
90
+ # Stops the processing thread, EM thread, and the websocket.
91
+ #
92
+ def stop!
93
+ @processing_thread.kill
94
+ @em_thread.kill
95
+ @websocket.stop!
96
+ end
97
+
98
+ # Start and stop the Orderbook. Used to reset Orderbook state with a fresh
99
+ # snapshot.
100
+ #
101
+ def reset!
102
+ stop!
103
+ start!
104
+ end
105
+
106
+ def on_message(&block)
107
+ @on_message = block
75
108
  end
76
109
 
77
110
  private
78
111
 
79
- def setup_websocket
80
- @websocket.message do |message|
81
- apply(message)
82
- @callback && @callback.call(message)
83
- end
112
+ # Converts an order array from the API into a hash.
113
+ #
114
+ def order_to_hash(price, size, order_id)
115
+ { price: BigDecimal.new(price),
116
+ size: BigDecimal.new(size),
117
+ order_id: order_id
118
+ }
84
119
  end
85
120
 
86
- def fetch_current_orderbook
87
- order_to_hash = lambda do |price, size, order_id|
88
- { price: BigDecimal.new(price),
89
- size: BigDecimal.new(size),
90
- order_id: order_id
91
- }
92
- end
121
+ # Fetch orderbook snapshot from API and convert order arrays to hashes.
122
+ #
123
+ def apply_orderbook_snapshot
93
124
  @client.orderbook(level: 3) do |resp|
94
- @bids = resp['bids'].map(&order_to_hash)
95
- @asks = resp['asks'].map(&order_to_hash)
96
- @first_sequence = resp['sequence']
125
+ @bids = resp['bids'].map { |b| order_to_hash(*b) }
126
+ @asks = resp['asks'].map { |a| order_to_hash(*a) }
127
+ @snapshot_sequence = resp['sequence']
128
+ @last_sequence = resp['sequence']
129
+ end
130
+ end
131
+
132
+ def setup_websocket_callback
133
+ @websocket.message do |message|
134
+ @queue.push(message)
97
135
  end
98
136
  end
99
137
 
100
- def ping
138
+ def setup_ping_timer
101
139
  EM.add_periodic_timer(PING_INTERVAL) do
102
140
  @websocket.ping do
103
141
  @last_pong = Time.now
@@ -105,19 +143,29 @@ class Orderbook
105
143
  end
106
144
  end
107
145
 
108
- def handle_errors
146
+ def setup_error_handler
109
147
  EM.error_handler do |e|
110
148
  print "Websocket Error: #{e.message} - #{e.backtrace.join("\n")}"
111
149
  end
112
150
  end
113
151
 
114
- def start_thread
115
- @thread = Thread.new do
152
+ def start_em_thread
153
+ @em_thread = Thread.new do
154
+ setup_websocket_callback
116
155
  EM.run do
117
- fetch_current_orderbook
118
156
  @websocket.start!
119
- ping
120
- handle_errors
157
+ setup_ping_timer
158
+ setup_error_handler
159
+ end
160
+ end
161
+ end
162
+
163
+ def start_processing_thread
164
+ @processing_thread = Thread.new do
165
+ loop do
166
+ message = @queue.shift
167
+ apply(message)
168
+ @on_message.call(message) unless @on_message.nil?
121
169
  end
122
170
  end
123
171
  end
@@ -10,7 +10,11 @@ class Orderbook
10
10
  # @bids, @asks, and @last_sequence.
11
11
  #
12
12
  def apply(msg)
13
- return if msg.fetch('sequence') <= @first_sequence
13
+ return if msg.fetch('sequence') != @last_sequence + 1
14
+ #if msg.fetch('sequence') != @last_sequence + 1
15
+ # puts "Expected #{@last_sequence + 1}, got #{msg.fetch('sequence')}"
16
+ # @websocket.stop!
17
+ #end
14
18
  @last_sequence = msg.fetch('sequence')
15
19
  BIGDECIMAL_KEYS.each do |key|
16
20
  msg[key] = BigDecimal.new(msg.fetch(key)) if msg.fetch(key, false)
@@ -1,5 +1,5 @@
1
1
  # Orderbook version number. I try to keep it semantic.
2
2
  #
3
3
  class Orderbook
4
- VERSION = '2.0.2'
4
+ VERSION = '3.0.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: orderbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Rodrigues
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-04 00:00:00.000000000 Z
11
+ date: 2015-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -57,7 +57,6 @@ files:
57
57
  - lib/orderbook.rb
58
58
  - lib/orderbook/book_analysis.rb
59
59
  - lib/orderbook/book_methods.rb
60
- - lib/orderbook/real_time_book.rb
61
60
  - lib/orderbook/version.rb
62
61
  - orderbook.gemspec
63
62
  homepage: https://github.com/mikerodrigues/orderbook
@@ -1,4 +0,0 @@
1
- class Orderbook
2
- # For backwards compatability
3
- class RealTimeBook < Orderbook; end
4
- end