coinbase-exchange 0.1.1
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 +7 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +21 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +72 -0
- data/LICENSE.txt +21 -0
- data/README.md +446 -0
- data/Rakefile +7 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/coinbase-exchange.gemspec +29 -0
- data/lib/coinbase/exchange.rb +21 -0
- data/lib/coinbase/exchange/adapters/em_http.rb +77 -0
- data/lib/coinbase/exchange/adapters/net_http.rb +65 -0
- data/lib/coinbase/exchange/api_client.rb +290 -0
- data/lib/coinbase/exchange/api_object.rb +39 -0
- data/lib/coinbase/exchange/api_response.rb +29 -0
- data/lib/coinbase/exchange/client.rb +9 -0
- data/lib/coinbase/exchange/errors.rb +42 -0
- data/lib/coinbase/exchange/version.rb +6 -0
- data/lib/coinbase/exchange/websocket.rb +119 -0
- metadata +176 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Coinbase
|
|
2
|
+
module Exchange
|
|
3
|
+
# Response item abstract model
|
|
4
|
+
class APIObject < Hash
|
|
5
|
+
def initialize(data)
|
|
6
|
+
super
|
|
7
|
+
update(data)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def update(data)
|
|
11
|
+
return if data.nil?
|
|
12
|
+
data.each { |key, val| self[key] = val } if data.is_a?(Hash)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def format(var)
|
|
16
|
+
return if var.nil?
|
|
17
|
+
# Looks like a number or currency
|
|
18
|
+
if var =~ /^.{0,1}\s*[0-9,]*\.{0,1}[0-9]*$/
|
|
19
|
+
BigDecimal(var.gsub(/[^0-9\.]/, ''))
|
|
20
|
+
else
|
|
21
|
+
var
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# This is common enough that we should override the hash method
|
|
26
|
+
def size
|
|
27
|
+
format(self['size']) || super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def method_missing(method, *args, &blk)
|
|
31
|
+
format(self[method.to_s]) || super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def respond_to_missing?(method, include_all = false)
|
|
35
|
+
self.key?(method.to_s) || super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Coinbase
|
|
2
|
+
module Exchange
|
|
3
|
+
# Encapsulate data for an API response
|
|
4
|
+
class APIResponse
|
|
5
|
+
attr_reader :received_at
|
|
6
|
+
|
|
7
|
+
def initialize(resp)
|
|
8
|
+
@received_at = Time.now
|
|
9
|
+
@response = resp
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def raw
|
|
13
|
+
@response
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def body
|
|
17
|
+
fail NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def headers
|
|
21
|
+
fail NotImplementedError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def status
|
|
25
|
+
fail NotImplementedError
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Coinbase
|
|
2
|
+
module Exchange
|
|
3
|
+
#
|
|
4
|
+
# Websocket Errors
|
|
5
|
+
#
|
|
6
|
+
class WebsocketError < RuntimeError
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class DroppedPackets < WebsocketError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# Rest API Errors
|
|
14
|
+
#
|
|
15
|
+
class APIError < RuntimeError
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Status 400
|
|
19
|
+
class BadRequestError < APIError
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Status 401
|
|
23
|
+
class NotAuthorizedError < APIError
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Status 403
|
|
27
|
+
class ForbiddenError < APIError
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Status 404
|
|
31
|
+
class NotFoundError < APIError
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Status 429
|
|
35
|
+
class RateLimitError < APIError
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Status 500
|
|
39
|
+
class InternalServerError < APIError
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
module Coinbase
|
|
2
|
+
module Exchange
|
|
3
|
+
# Websocket client for Coinbase Exchange
|
|
4
|
+
class Websocket
|
|
5
|
+
def initialize(options = {})
|
|
6
|
+
@ws_url = options[:ws_url] || "wss://ws-feed.exchange.coinbase.com"
|
|
7
|
+
@product = options[:product_id] || 'BTC-USD'
|
|
8
|
+
@keepalive = options[:keepalive] || false
|
|
9
|
+
|
|
10
|
+
@message_cb = ->(_data) { nil }
|
|
11
|
+
@received_cb = ->(_data) { nil }
|
|
12
|
+
@open_cb = ->(_data) { nil }
|
|
13
|
+
@match_cb = ->(_data) { nil }
|
|
14
|
+
@change_cb = ->(_data) { nil }
|
|
15
|
+
@done_cb = ->(_data) { nil }
|
|
16
|
+
@error_cb = ->(_data) { nil }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def start!
|
|
20
|
+
if EventMachine.reactor_running?
|
|
21
|
+
@reactor_owner = false
|
|
22
|
+
refresh!
|
|
23
|
+
else
|
|
24
|
+
@reactor_owner = true
|
|
25
|
+
EM.run { refresh! }
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def stop!
|
|
30
|
+
if @reactor_owner == true
|
|
31
|
+
@socket.onclose = ->(_event) { EM.stop }
|
|
32
|
+
else
|
|
33
|
+
@socket.onclose = ->(_event) { nil }
|
|
34
|
+
end
|
|
35
|
+
@socket.close
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def refresh!
|
|
39
|
+
@socket = Faye::WebSocket::Client.new(@ws_url)
|
|
40
|
+
@socket.onopen = method(:ws_opened)
|
|
41
|
+
@socket.onmessage = method(:ws_received)
|
|
42
|
+
@socket.onclose = method(:ws_closed)
|
|
43
|
+
@socket.onerror = method(:ws_error)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def subscribe!(options = {})
|
|
47
|
+
product = options[:product_id] || @product
|
|
48
|
+
@socket.send({ type: 'subscribe', product_id: product }.to_json)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def ping(options = {})
|
|
52
|
+
msg = options[:payload] || Time.now.to_s
|
|
53
|
+
@socket.ping(msg) do |resp|
|
|
54
|
+
yield(resp) if block_given?
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Run this before processing every message
|
|
59
|
+
def message(&block)
|
|
60
|
+
@message_cb = block
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def received(&block)
|
|
64
|
+
@received_cb = block
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def open(&block)
|
|
68
|
+
@open_cb = block
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def match(&block)
|
|
72
|
+
@match_cb = block
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def change(&block)
|
|
76
|
+
@change_cb = block
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def done(&block)
|
|
80
|
+
@done_cb = block
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def error(&block)
|
|
84
|
+
@error_cb = block
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def ws_opened(_event)
|
|
90
|
+
subscribe!
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def ws_received(event)
|
|
94
|
+
data = APIObject.new(JSON.parse(event.data))
|
|
95
|
+
@message_cb.call(data)
|
|
96
|
+
case data['type']
|
|
97
|
+
when 'received' then @received_cb.call(data)
|
|
98
|
+
when 'open' then @open_cb.call(data)
|
|
99
|
+
when 'match' then @match_cb.call(data)
|
|
100
|
+
when 'change' then @change_cb.call(data)
|
|
101
|
+
when 'done' then @done_cb.call(data)
|
|
102
|
+
when 'error' then @error_cb.call(data)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def ws_closed(_event)
|
|
107
|
+
if @keepalive
|
|
108
|
+
refresh!
|
|
109
|
+
else
|
|
110
|
+
EM.stop
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def ws_error(event)
|
|
115
|
+
fail WebsocketError, event.data
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: coinbase-exchange
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- John Duhamel
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-06-15 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bigdecimal
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: em-http-request
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: faye-websocket
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: bundler
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.8'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.8'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rake
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '10.0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '10.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: rspec
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: webmock
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: pry
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
description:
|
|
126
|
+
email:
|
|
127
|
+
- john.duhamel@coinbase.com
|
|
128
|
+
executables: []
|
|
129
|
+
extensions: []
|
|
130
|
+
extra_rdoc_files: []
|
|
131
|
+
files:
|
|
132
|
+
- ".gitignore"
|
|
133
|
+
- ".rubocop.yml"
|
|
134
|
+
- Gemfile
|
|
135
|
+
- Gemfile.lock
|
|
136
|
+
- LICENSE.txt
|
|
137
|
+
- README.md
|
|
138
|
+
- Rakefile
|
|
139
|
+
- bin/console
|
|
140
|
+
- bin/setup
|
|
141
|
+
- coinbase-exchange.gemspec
|
|
142
|
+
- lib/coinbase/exchange.rb
|
|
143
|
+
- lib/coinbase/exchange/adapters/em_http.rb
|
|
144
|
+
- lib/coinbase/exchange/adapters/net_http.rb
|
|
145
|
+
- lib/coinbase/exchange/api_client.rb
|
|
146
|
+
- lib/coinbase/exchange/api_object.rb
|
|
147
|
+
- lib/coinbase/exchange/api_response.rb
|
|
148
|
+
- lib/coinbase/exchange/client.rb
|
|
149
|
+
- lib/coinbase/exchange/errors.rb
|
|
150
|
+
- lib/coinbase/exchange/version.rb
|
|
151
|
+
- lib/coinbase/exchange/websocket.rb
|
|
152
|
+
homepage: https://github.com/coinbase/coinbase-exchange-ruby
|
|
153
|
+
licenses:
|
|
154
|
+
- MIT
|
|
155
|
+
metadata: {}
|
|
156
|
+
post_install_message:
|
|
157
|
+
rdoc_options: []
|
|
158
|
+
require_paths:
|
|
159
|
+
- lib
|
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
161
|
+
requirements:
|
|
162
|
+
- - ">="
|
|
163
|
+
- !ruby/object:Gem::Version
|
|
164
|
+
version: '0'
|
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
166
|
+
requirements:
|
|
167
|
+
- - ">="
|
|
168
|
+
- !ruby/object:Gem::Version
|
|
169
|
+
version: '0'
|
|
170
|
+
requirements: []
|
|
171
|
+
rubyforge_project:
|
|
172
|
+
rubygems_version: 2.2.2
|
|
173
|
+
signing_key:
|
|
174
|
+
specification_version: 4
|
|
175
|
+
summary: Client library for Coinbase Exchange
|
|
176
|
+
test_files: []
|