bitopro 0.1.1 → 1.0.0

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
2
  SHA256:
3
- metadata.gz: 592f45307f73b0a481ac447c06b61154a1fa302b13522de77b0d2c7439fe9511
4
- data.tar.gz: 7835a701ec02aa883e3a168c7db6690adef5d95d5d7a045180b6e4e9499cc2f0
3
+ metadata.gz: 400df4fabd57efac65d8ce91cf6b8b1f6f5175dfecd142e0639f7134cb83a466
4
+ data.tar.gz: '06871ef22a043496f61aac115ecdd869d2187749864cdd725c44e0a87f2554fb'
5
5
  SHA512:
6
- metadata.gz: 7eaa44e533d2788c03394d7aa725017e3b0396446072b1e30f4a7801a85b2d5bcb2c09056cc47baa90c5d290b5c87d84b3374e648b9618c2900d46a4023a94a5
7
- data.tar.gz: b523c8cb40a295c8efa6d2f9b584206b56402b02cedd3a4d11b149e1fd73c9a7c2bc5e492ac2cba0c978525b34691c5a0a20960d85fcb96911716a9207784738
6
+ metadata.gz: a7a76aaf65109f2a8273c1234189c6fef9ab649e2e70d07bf25058622c4fb06a33d6046a29432a4b42f5bf7ef29a8ac6b916d7cfa50ef950e2d3eaf0355272f2
7
+ data.tar.gz: c2aab8d98acbf5b6ea015ec41596f6902c53dc57d5fbf47f7980a29c96869cef6c0858f5b2251bb8a5e8689b87a9a7fd803d07088df6b3f9b5988cc4ea1f7997
data/lib/bitopro.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require "rest-client"
2
2
  require "openssl"
3
3
  require "json"
4
+ require "base64"
4
5
 
5
6
  require "bitopro/version"
7
+ require "bitopro/client"
6
8
  require "bitopro/config"
7
- require "bitopro/config/validator"
8
9
 
9
10
  module Bitopro
10
11
  BASE_URL = "https://api.bitopro.com/v2".freeze
@@ -13,7 +14,7 @@ module Bitopro
13
14
  # Configures the Bitopro API.
14
15
  #
15
16
  # @example
16
- # Bitopro.configure do |c|
17
+ # Bitopro.configure do |config|
17
18
  # config.key = 113743
18
19
  # config.secret = 'fd04e13d806a90f96614ad8e529b2822'
19
20
  # end
@@ -24,17 +25,7 @@ module Bitopro
24
25
  def configured?
25
26
  Bitopro::Config.instance.valid?
26
27
  end
27
-
28
-
29
- end
30
-
31
- def self.order_book(currency_pair)
32
- JSON.parse(resource["order-book"][currency_pair].get)
33
28
  end
29
+ end
34
30
 
35
- protected
36
31
 
37
- def self.resource
38
- @resource ||= RestClient::Resource.new(BASE_URL)
39
- end
40
- end
@@ -0,0 +1,15 @@
1
+ module Bitopro
2
+ module Account
3
+ def account_balance
4
+ authenticated_get("/accounts/balance")
5
+ end
6
+
7
+ def order_history
8
+ authenticated_get("/orders/history")
9
+ end
10
+
11
+ def order_list(pair: "", page: 1, active: false)
12
+ authenticated_get("/orders/#{currency_pair}", params: { page: page, active: active })
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module Bitopro
2
+ module Order
3
+ # Example:
4
+ # client = Bitopro::Client.new
5
+ # client.create_order(pair: "bito_eth", action: "buy", amount: "600", price: "0.000001", type: "limit")
6
+ def create_order(pair: "", action: "", amount: 0, price: 0, type: "limit")
7
+ authenticated_post("/orders/#{pair}", { body: { action: action,
8
+ amount: amount,
9
+ price: price,
10
+ timestamp: timestamp,
11
+ type: type }})
12
+ end
13
+
14
+ # Example:
15
+ # client = Bitopro::Client.new
16
+ # client.cancel_order(pair: "bito_eth", order_id: "3135725012")
17
+ def cancel_order(pair: "", order_id: "")
18
+ authenticated_delete("/orders/#{pair}/#{order_id}")
19
+ end
20
+
21
+ def get_order(pair: "", order_id: "")
22
+ authenticated_get("/orders/#{pair}/#{order_id}")
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ module Bitopro
2
+ module Public
3
+ def order_book(currency_pair)
4
+ get("/order-book/#{currency_pair}")
5
+ end
6
+
7
+ def ticker(currency_pair)
8
+ get("/ticker/#{currency_pair}")
9
+ end
10
+
11
+ def recent_trades(currency_pair)
12
+ get("/trades/#{currency_pair}")
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,109 @@
1
+ require_relative './api/account'
2
+ require_relative './api/public'
3
+ require_relative './api/order'
4
+
5
+ module Bitopro
6
+ class Client
7
+ class BitoproSetupError < StandardError; end
8
+
9
+ BASE_URL = "https://api.bitopro.com/v2".freeze
10
+
11
+ include Bitopro::Public
12
+ include Bitopro::Account
13
+ include Bitopro::Order
14
+
15
+ def initialize
16
+ @config = Bitopro::Config.instance
17
+ end
18
+
19
+ private
20
+
21
+ def build_url(url)
22
+ "#{BASE_URL}#{url}"
23
+ end
24
+
25
+ def authenticated_get(url, params = {})
26
+ raise BitoproSetupError, "Must be set configure before call authenticate action" unless Bitopro.configured?
27
+
28
+ complete_url = build_url(url)
29
+
30
+ json_body = { identity: @config.email, nonce: timestamp }.to_json
31
+ payload = build_payload(json_body)
32
+ headers = build_headers(payload)
33
+
34
+ response = RestClient::Request.execute(method: :get,
35
+ url: complete_url,
36
+ headers: headers,
37
+ payload: params,
38
+ timeout: 10)
39
+
40
+ JSON.parse(response.body)
41
+ end
42
+
43
+ def authenticated_post(url, params = {})
44
+ raise BitoproSetupError, "Must be set configure before call authenticate action" unless Bitopro.configured?
45
+
46
+ complete_url = build_url(url)
47
+
48
+ json_body = params[:body].to_json
49
+ payload = build_payload(json_body)
50
+ headers = build_headers(payload)
51
+
52
+ response = RestClient::Request.execute(method: :post,
53
+ url: complete_url,
54
+ headers: headers,
55
+ payload: json_body,
56
+ timeout: 10)
57
+
58
+ JSON.parse(response.body)
59
+ end
60
+
61
+ def authenticated_delete(url, params = {})
62
+ raise BitoproSetupError, "Must be set configure before call authenticate action" unless Bitopro.configured?
63
+
64
+ complete_url = build_url(url)
65
+
66
+ json_body = { identity: @config.email, nonce: timestamp }.to_json
67
+ payload = build_payload(json_body)
68
+ headers = build_headers(payload)
69
+
70
+ response = RestClient::Request.execute(method: :delete,
71
+ url: complete_url,
72
+ headers: headers,
73
+ payload: params,
74
+ timeout: 10)
75
+
76
+ JSON.parse(response.body)
77
+ end
78
+
79
+ def get(url, params = {})
80
+ complete_url = build_url(url)
81
+ response = RestClient::Request.execute(method: :get,
82
+ url: complete_url,
83
+ payload: params,
84
+ timeout: 10)
85
+
86
+ JSON.parse(response.body)
87
+ end
88
+
89
+ def build_payload(json_body)
90
+ Base64.strict_encode64(json_body)
91
+ end
92
+
93
+ def timestamp
94
+ (Time.now.to_f * 1000).floor
95
+ end
96
+
97
+ def signature(payload)
98
+ OpenSSL::HMAC.hexdigest("SHA384", @config.secret, payload)
99
+ end
100
+
101
+ def build_headers(payload)
102
+ {
103
+ "X-BITOPRO-APIKEY": @config.key,
104
+ "X-BITOPRO-PAYLOAD": payload,
105
+ "X-BITOPRO-SIGNATURE": signature(payload)
106
+ }
107
+ end
108
+ end
109
+ end
@@ -12,14 +12,16 @@ module Bitopro
12
12
 
13
13
  attr_accessor :key
14
14
  attr_accessor :secret
15
+ attr_accessor :email
15
16
 
16
17
  def initialize(user_config = {})
17
18
  self.key = user_config[:key]
18
19
  self.secret = user_config[:secret]
20
+ self.email = user_config[:email]
19
21
  end
20
22
 
21
23
  def valid?
22
- Validator.validate(self)
24
+ !self.key.empty? && !self.secret.empty? && !self.email.empty? rescue false
23
25
  end
24
26
  end
25
27
  end
@@ -1,3 +1,3 @@
1
1
  module Bitopro
2
- VERSION = "0.1.1".freeze
2
+ VERSION = "1.0.0".freeze
3
3
  end
@@ -13,12 +13,39 @@ RSpec.describe Bitopro do
13
13
  it "makes Bitopro configured" do
14
14
  expect(described_class).not_to be_configured
15
15
 
16
- described_class.configure do |c|
17
- c.key = 1
18
- c.secret = '2'
16
+ described_class.configure do |config|
17
+ config.email = "bitopro@ruby.rspec"
18
+ config.key = "1"
19
+ config.secret = "2"
19
20
  end
20
21
 
21
22
  expect(described_class).to be_configured
22
23
  end
23
24
  end
25
+
26
+ describe "#order_book" do
27
+ let(:currency_pair) { "btc_twd" }
28
+
29
+ subject { Bitopro::Client.new.order_book(currency_pair) }
30
+
31
+ context "when currency pair is empty" do
32
+
33
+ end
34
+
35
+ context "when api responds with error" do
36
+
37
+ end
38
+
39
+ context "when api succeeds" do
40
+ let!(:request_stub) do
41
+ stub_request(:get, "#{Bitopro::BASE_URL}/order-book/#{currency_pair}")
42
+ .to_return(status: 200, body: json_fixture('order-book'))
43
+ end
44
+
45
+ it "return parsed json order book" do
46
+ subject
47
+ expect(request_stub).to have_been_requested
48
+ end
49
+ end
50
+ end
24
51
  end
@@ -0,0 +1,246 @@
1
+ {
2
+ "asks": [
3
+ {
4
+ "amount": "1",
5
+ "count": 1,
6
+ "price": "160990",
7
+ "total": "1"
8
+ },
9
+ {
10
+ "amount": "0.141351",
11
+ "count": 1,
12
+ "price": "161000",
13
+ "total": "1.141351"
14
+ },
15
+ {
16
+ "amount": "0.011",
17
+ "count": 1,
18
+ "price": "161899",
19
+ "total": "1.152351"
20
+ },
21
+ {
22
+ "amount": "0.011",
23
+ "count": 1,
24
+ "price": "161929",
25
+ "total": "1.163351"
26
+ },
27
+ {
28
+ "amount": "0.011",
29
+ "count": 1,
30
+ "price": "161979",
31
+ "total": "1.174351"
32
+ },
33
+ {
34
+ "amount": "0.00088542",
35
+ "count": 1,
36
+ "price": "161986",
37
+ "total": "1.17523642"
38
+ },
39
+ {
40
+ "amount": "0.038",
41
+ "count": 2,
42
+ "price": "161999",
43
+ "total": "1.21323642"
44
+ },
45
+ {
46
+ "amount": "0.011",
47
+ "count": 1,
48
+ "price": "162489",
49
+ "total": "1.22423642"
50
+ },
51
+ {
52
+ "amount": "0.1",
53
+ "count": 1,
54
+ "price": "162490",
55
+ "total": "1.32423642"
56
+ },
57
+ {
58
+ "amount": "0.011",
59
+ "count": 1,
60
+ "price": "162498",
61
+ "total": "1.33523642"
62
+ },
63
+ {
64
+ "amount": "0.099",
65
+ "count": 1,
66
+ "price": "162499",
67
+ "total": "1.43423642"
68
+ },
69
+ {
70
+ "amount": "0.02744999",
71
+ "count": 1,
72
+ "price": "162500",
73
+ "total": "1.46168641"
74
+ },
75
+ {
76
+ "amount": "0.099",
77
+ "count": 1,
78
+ "price": "162589",
79
+ "total": "1.56068641"
80
+ },
81
+ {
82
+ "amount": "0.55968182",
83
+ "count": 5,
84
+ "price": "162599",
85
+ "total": "2.12036823"
86
+ },
87
+ {
88
+ "amount": "0.099",
89
+ "count": 1,
90
+ "price": "162799",
91
+ "total": "2.21936823"
92
+ },
93
+ {
94
+ "amount": "1.2213",
95
+ "count": 2,
96
+ "price": "162800",
97
+ "total": "3.44066823"
98
+ },
99
+ {
100
+ "amount": "0.11",
101
+ "count": 1,
102
+ "price": "162989",
103
+ "total": "3.55066823"
104
+ },
105
+ {
106
+ "amount": "2.06791042",
107
+ "count": 3,
108
+ "price": "162999",
109
+ "total": "5.61857865"
110
+ },
111
+ {
112
+ "amount": "1.4",
113
+ "count": 2,
114
+ "price": "163000",
115
+ "total": "7.01857865"
116
+ },
117
+ {
118
+ "amount": "0.1",
119
+ "count": 1,
120
+ "price": "163100",
121
+ "total": "7.11857865"
122
+ }
123
+ ],
124
+ "bids": [
125
+ {
126
+ "amount": "0.03324",
127
+ "count": 1,
128
+ "price": "160800",
129
+ "total": "0.03324"
130
+ },
131
+ {
132
+ "amount": "0.74529743",
133
+ "count": 1,
134
+ "price": "160799",
135
+ "total": "0.77853743"
136
+ },
137
+ {
138
+ "amount": "0.01400139",
139
+ "count": 1,
140
+ "price": "160700",
141
+ "total": "0.79253882"
142
+ },
143
+ {
144
+ "amount": "0.04",
145
+ "count": 1,
146
+ "price": "160300",
147
+ "total": "0.83253882"
148
+ },
149
+ {
150
+ "amount": "0.13875606",
151
+ "count": 1,
152
+ "price": "160100",
153
+ "total": "0.97129488"
154
+ },
155
+ {
156
+ "amount": "0.1",
157
+ "count": 1,
158
+ "price": "160001",
159
+ "total": "1.07129488"
160
+ },
161
+ {
162
+ "amount": "0.23325526",
163
+ "count": 2,
164
+ "price": "160000",
165
+ "total": "1.30455014"
166
+ },
167
+ {
168
+ "amount": "0.07854057",
169
+ "count": 1,
170
+ "price": "159022",
171
+ "total": "1.38309071"
172
+ },
173
+ {
174
+ "amount": "0.5",
175
+ "count": 1,
176
+ "price": "159000",
177
+ "total": "1.88309071"
178
+ },
179
+ {
180
+ "amount": "0.02",
181
+ "count": 1,
182
+ "price": "158200",
183
+ "total": "1.90309071"
184
+ },
185
+ {
186
+ "amount": "0.1",
187
+ "count": 1,
188
+ "price": "158100",
189
+ "total": "2.00309071"
190
+ },
191
+ {
192
+ "amount": "0.23093353",
193
+ "count": 3,
194
+ "price": "158000",
195
+ "total": "2.23402424"
196
+ },
197
+ {
198
+ "amount": "0.67424151",
199
+ "count": 5,
200
+ "price": "157000",
201
+ "total": "2.90826575"
202
+ },
203
+ {
204
+ "amount": "0.1",
205
+ "count": 1,
206
+ "price": "156900",
207
+ "total": "3.00826575"
208
+ },
209
+ {
210
+ "amount": "0.00269",
211
+ "count": 1,
212
+ "price": "156850",
213
+ "total": "3.01095575"
214
+ },
215
+ {
216
+ "amount": "0.5666454",
217
+ "count": 2,
218
+ "price": "156800",
219
+ "total": "3.57760115"
220
+ },
221
+ {
222
+ "amount": "0.03189647",
223
+ "count": 1,
224
+ "price": "156765",
225
+ "total": "3.60949762"
226
+ },
227
+ {
228
+ "amount": "0.1",
229
+ "count": 1,
230
+ "price": "156650",
231
+ "total": "3.70949762"
232
+ },
233
+ {
234
+ "amount": "0.58114303",
235
+ "count": 1,
236
+ "price": "156600",
237
+ "total": "4.29064065"
238
+ },
239
+ {
240
+ "amount": "0.05",
241
+ "count": 1,
242
+ "price": "156500",
243
+ "total": "4.34064065"
244
+ }
245
+ ]
246
+ }
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "rspec"
2
+ require "rest-client"
2
3
  require "bundler/setup"
3
4
  require 'webmock/rspec'
4
5
  require 'pry'
@@ -7,7 +8,6 @@ require 'bitopro'
7
8
  Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each { |file| require file }
8
9
 
9
10
  WebMock.disable_net_connect!
10
-
11
11
  RSpec.configure do |config|
12
12
  # Enable flags like --only-failures and --next-failure
13
13
  config.example_status_persistence_file_path = ".rspec_status"
@@ -15,6 +15,8 @@ RSpec.configure do |config|
15
15
  # Disable RSpec exposing methods globally on `Module` and `main`
16
16
  config.disable_monkey_patching!
17
17
 
18
+ config.include RSpecHelpers
19
+
18
20
  config.expect_with :rspec do |c|
19
21
  c.syntax = :expect
20
22
  end
@@ -0,0 +1,13 @@
1
+ module RSpecHelpers
2
+ def file_fixture(path)
3
+ File.read(File.join('spec', 'fixtures', path))
4
+ end
5
+
6
+ def is_expected_block
7
+ expect { subject }
8
+ end
9
+
10
+ def json_fixture(fixture_name)
11
+ file_fixture("#{fixture_name}.json")
12
+ end
13
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitopro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - niclin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-17 00:00:00.000000000 Z
11
+ date: 2019-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rest-client
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: Ruby wrapper for the Bitopro API.
84
98
  email:
85
99
  - bboyceo@hotmail.com
@@ -88,11 +102,16 @@ extensions: []
88
102
  extra_rdoc_files: []
89
103
  files:
90
104
  - lib/bitopro.rb
105
+ - lib/bitopro/api/account.rb
106
+ - lib/bitopro/api/order.rb
107
+ - lib/bitopro/api/public.rb
108
+ - lib/bitopro/client.rb
91
109
  - lib/bitopro/config.rb
92
- - lib/bitopro/config/validator.rb
93
110
  - lib/bitopro/version.rb
94
111
  - spec/bitopro/api_spec.rb
112
+ - spec/fixtures/order-book.json
95
113
  - spec/spec_helper.rb
114
+ - spec/support/rspec_helpers.rb
96
115
  homepage: https://github.com/niclin/bitopro
97
116
  licenses:
98
117
  - MIT
@@ -118,7 +137,9 @@ rubyforge_project:
118
137
  rubygems_version: 2.7.8
119
138
  signing_key:
120
139
  specification_version: 4
121
- summary: bitopro-0.1.1
140
+ summary: bitopro-1.0.0
122
141
  test_files:
123
142
  - spec/spec_helper.rb
124
143
  - spec/bitopro/api_spec.rb
144
+ - spec/support/rspec_helpers.rb
145
+ - spec/fixtures/order-book.json
@@ -1,35 +0,0 @@
1
- module Bitopro
2
- class Config
3
- class Validator
4
- class << self
5
- Error = Class.new(StandardError)
6
-
7
- def validate(config)
8
- errors = []
9
-
10
- unless valid_key?(config)
11
- errors << 'key is required'
12
- end
13
- unless valid_secret?(config)
14
- errors << 'secret is required'
15
- end
16
-
17
- errors.empty?
18
- end
19
-
20
- private
21
-
22
- def valid_key?(config)
23
- return true if config.key.to_i > 0
24
- false
25
- end
26
-
27
- def valid_secret?(config)
28
- return false unless config.secret.is_a?(String)
29
- return false if config.secret.empty?
30
- true
31
- end
32
- end
33
- end
34
- end
35
- end