kaesen 0.1.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.
@@ -0,0 +1,110 @@
1
+ require_relative 'market.rb'
2
+ require 'net/http'
3
+ require 'openssl'
4
+ require 'json'
5
+ require 'date'
6
+ require 'bigdecimal'
7
+
8
+ module Kaesen
9
+ # Kaesen Wrapper Class
10
+ # https://www.monetago.com/#/api/
11
+ ## API制限
12
+ ## . More than 500 requests per 10 minutes will result in IP ban.
13
+ ## . For real-time data please refer to the MonetaGo WebSocket API.
14
+
15
+ class Monetago < Market
16
+ @@nonce = 0
17
+
18
+ def initialize()
19
+ super()
20
+ @name = "Monetago"
21
+ @api_key = ENV["MONETAGO_KEY"]
22
+ @api_secret = ENV["MONETAGO_SECRET"]
23
+ @url_public = "https://api.monetago.com:8400/ajax/v1"
24
+ @url_private = @url_public
25
+ end
26
+
27
+ #############################################################
28
+ # API for public information
29
+ #############################################################
30
+
31
+ # Get ticker information.
32
+ # @return [hash] ticker
33
+ # ask: [BigDecimal] 最良売気配値
34
+ # bid: [BigDecimal] 最良買気配値
35
+ # last: [BigDecimal] 最近値(?用語要チェック), last price
36
+ # high: [BigDecimal] 高値
37
+ # low: [BigDecimal] 安値
38
+ # volume: [BigDecimal] 取引量
39
+ # ltimestamp: [int] ローカルタイムスタンプ
40
+ # timestamp: [int] タイムスタンプ
41
+ def ticker
42
+ h = post_ssl(@url_public + "/GetTicker", {product_pair: 'BTCJPY'})
43
+ {
44
+ "ask" => BigDecimal.new(h["ask"].to_s),
45
+ "bid" => BigDecimal.new(h["bid"].to_s),
46
+ "last" => BigDecimal.new(h["last"].to_s),
47
+ "high" => BigDecimal.new(h["high"].to_s),
48
+ "low" => BigDecimal.new(h["low"].to_s),
49
+ "volume" => BigDecimal.new(h["volume"].to_s),
50
+ "ltimestamp" => Time.now.to_i,
51
+ }
52
+ end
53
+
54
+ # Get order book.
55
+ # @abstract
56
+ # @return [hash] array of market depth
57
+ # asks: [Array] 売りオーダー
58
+ # price : [BigDecimal]
59
+ # size : [BigDecimal]
60
+ # bids: [Array] 買いオーダー
61
+ # price : [BigDecimal]
62
+ # size : [BigDecimal]
63
+ # ltimestamp: [int] ローカルタイムスタンプ
64
+ def depth
65
+ h = post_ssl(@url_public + "/GetOrderBook")
66
+ {
67
+ "asks" => h["asks"].map{|x| [BigDecimal.new(x["px"].to_s), BigDecimal.new(x["qty"].to_s)]},
68
+ "bids" => h["bids"].map{|x| [BigDecimal.new(x["px"].to_s), BigDecimal.new(x["qty"].to_s)]},
69
+ "ltimestamp" => Time.now.to_i,
70
+ }
71
+ end
72
+
73
+ private
74
+
75
+ def initialize_https(uri)
76
+ https = Net::HTTP.new(uri.host, uri.port)
77
+ https.use_ssl = true
78
+ https.open_timeout = 5
79
+ https.read_timeout = 15
80
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
81
+ https.verify_depth = 5
82
+ https
83
+ end
84
+
85
+ def post_ssl(address, params={})
86
+ uri = URI.parse(address)
87
+
88
+ begin
89
+ req = Net::HTTP::Post.new(uri, initheader = {"Content-Type" => "application/json"})
90
+ req.set_form(params)
91
+ req.body = {"productPair" => "BTCJPY"}.to_json
92
+
93
+ https = initialize_https(uri)
94
+ https.start {|w|
95
+ response = w.request(req)
96
+ case response
97
+ when Net::HTTPSuccess
98
+ json = JSON.parse(response.body)
99
+ return json
100
+ else
101
+ raise ConnectionFailedException, "Failed to connect to #{@name}: " + response.value
102
+ end
103
+ }
104
+ rescue
105
+ raise
106
+ end
107
+ end
108
+
109
+ end
110
+ end
@@ -0,0 +1,155 @@
1
+ require_relative 'market.rb'
2
+ require 'net/http'
3
+ require 'openssl'
4
+ require 'json'
5
+ require 'bigdecimal'
6
+
7
+ module Kaesen
8
+ # Quoine Wrapper Class
9
+ # https://developers.quoine.com/
10
+ ## API制限
11
+ # API users should not make more than 300 requests per 5 minute. Requests go beyond the limit will return with a 429 status
12
+
13
+ class Quoine < Market
14
+ @@nonce = 0
15
+
16
+ def initialize()
17
+ super()
18
+ @name = "Quoine"
19
+ @api_key = ENV["QUOINE_KEY"]
20
+ @api_secret = ENV["QUOINE_SECRET"]
21
+ @url_public = "https://api.quoine.com"
22
+ @url_private = @url_public
23
+ end
24
+
25
+ #############################################################
26
+ # API for public information
27
+ #############################################################
28
+
29
+ # Get ticker information.
30
+ # @return [hash] ticker
31
+ # ask: [BigDecimal] 最良売気配値
32
+ # bid: [BigDecimal] 最良買気配値
33
+ # last: [BigDecimal] 最近値(?用語要チェック), last price
34
+ # high: [BigDecimal] 高値
35
+ # low: [BigDecimal] 安値
36
+ # volume: [BigDecimal] 取引量
37
+ # ltimestamp: [int] ローカルタイムスタンプ
38
+ # vwap: [BigDecimal] 過去24時間の加重平均
39
+ def ticker
40
+ h = get_ssl(@url_public + "/products/code/CASH/BTCJPY") # the id of BTCJPY is 5.
41
+ {
42
+ "ask" => BigDecimal.new(h["market_ask"].to_s),
43
+ "bid" => BigDecimal.new(h["market_bid"].to_s),
44
+ "last" => BigDecimal.new(h["last_traded_price"].to_s),
45
+ # "high"
46
+ # "low"
47
+ "volume" => BigDecimal.new(h["volume_24h"].to_s), # of the previous 24 hours
48
+ "ltimestamp" => Time.now.to_i,
49
+ # "vwap"
50
+ }
51
+ end
52
+
53
+ # Get order book.
54
+ # @abstract
55
+ # @return [hash] array of market depth
56
+ # asks: [Array] 売りオーダー
57
+ # price : [BigDecimal]
58
+ # size : [BigDecimal]
59
+ # bids: [Array] 買いオーダー
60
+ # price : [BigDecimal]
61
+ # size : [BigDecimal]
62
+ # ltimestamp: [int] ローカルタイムスタンプ
63
+ def depth
64
+ h = get_ssl(@url_public + "/products/5/price_levels") # the id of BTCJPY is 5.
65
+ {
66
+ "asks" => h["buy_price_levels"].map{|a,b| [BigDecimal.new(a.to_s), BigDecimal.new(b.to_s)]}, # to_s でないと誤差が生じる
67
+ "bids" => h["sell_price_levels"].map{|a,b| [BigDecimal.new(a.to_s), BigDecimal.new(b.to_s)]}, # to_s でないと誤差が生じる
68
+ "ltimestamp" => Time.now.to_i,
69
+ }
70
+ end
71
+
72
+ private
73
+
74
+ def initialize_https(uri)
75
+ https = Net::HTTP.new(uri.host, uri.port)
76
+ https.use_ssl = true
77
+ https.open_timeout = 5
78
+ https.read_timeout = 15
79
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
80
+ https.verify_depth = 5
81
+ https
82
+ end
83
+
84
+ # Connect to address via https, and return json response.
85
+ def get_ssl(address)
86
+ uri = URI.parse(address)
87
+
88
+ begin
89
+ https = initialize_https(uri)
90
+ https.start {|w|
91
+ response = w.get(uri.request_uri)
92
+ case response
93
+ when Net::HTTPSuccess
94
+ json = JSON.parse(response.body)
95
+ raise JSONException, response.body if json == nil
96
+ return json
97
+ else
98
+ raise ConnectionFailedException, "Failed to connect to #{@name}."
99
+ end
100
+ }
101
+ rescue
102
+ raise
103
+ end
104
+ end
105
+
106
+ def get_nonce
107
+ pre_nonce = @@nonce
108
+ next_nonce = (Time.now.to_i) * 100
109
+
110
+ if next_nonce <= pre_nonce
111
+ @@nonce = pre_nonce + 1
112
+ else
113
+ @@nonce = next_nonce
114
+ end
115
+
116
+ return @@nonce
117
+ end
118
+
119
+ def get_sign(req)
120
+ secret = @api_secret
121
+ text = req.body
122
+
123
+ OpenSSL::HMAC::hexdigest(OpenSSL::Digest.new('sha512'), secret, text)
124
+ end
125
+
126
+ # Connect to address via https, and return json response.
127
+ def post_ssl(address, data={})
128
+ uri = URI.parse(address)
129
+ data["nonce"] = get_nonce
130
+
131
+ begin
132
+ req = Net::HTTP::Post.new(uri)
133
+ req.set_form_data(data)
134
+ req["Key"] = @api_key
135
+ req["Sign"] = get_sign(req)
136
+
137
+ https = initialize_https(uri)
138
+ https.start {|w|
139
+ response = w.request(req)
140
+ case response
141
+ when Net::HTTPSuccess
142
+ json = JSON.parse(response.body)
143
+ raise JSONException, response.body if json == nil
144
+ return json
145
+ else
146
+ raise ConnectionFailedException, "Failed to connect to #{@name}: " + response.value
147
+ end
148
+ }
149
+ rescue
150
+ raise
151
+ end
152
+ end
153
+
154
+ end
155
+ end
@@ -0,0 +1,3 @@
1
+ module Kaesen
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,382 @@
1
+ # coding: utf-8
2
+ require_relative 'market.rb'
3
+ require 'net/http'
4
+ require 'openssl'
5
+ require 'json'
6
+ require 'bigdecimal'
7
+
8
+ module Kaesen
9
+ # Zaif Wrapper Class
10
+ # https://corp.zaif.jp/api-docs/
11
+
12
+ class Zaif < Market
13
+ @@nonce = 0
14
+
15
+ def initialize()
16
+ super()
17
+ @name = "Zaif"
18
+ @api_key = ENV["ZAIF_KEY"]
19
+ @api_secret = ENV["ZAIF_SECRET"]
20
+ @url_public = "https://api.zaif.jp/api/1"
21
+ @url_private = "https://api.zaif.jp/tapi"
22
+ end
23
+
24
+ #############################################################
25
+ # API for public information
26
+ #############################################################
27
+
28
+ # Get ticker information.
29
+ # @return [hash] ticker
30
+ # ask: [BigDecimal] 最良売気配値
31
+ # bid: [BigDecimal] 最良買気配値
32
+ # last: [BigDecimal] 最近値(?用語要チェック), last price
33
+ # high: [BigDecimal] 高値
34
+ # low: [BigDecimal] 安値
35
+ # volume: [BigDecimal] 取引量
36
+ # ltimestamp: [int] ローカルタイムスタンプ
37
+ # vwap: [BigDecimal] 過去24時間の加重平均
38
+ def ticker
39
+ h = get_ssl(@url_public + "/ticker/btc_jpy")
40
+ {
41
+ "ask" => BigDecimal.new(h["ask"].to_s),
42
+ "bid" => BigDecimal.new(h["bid"].to_s),
43
+ "last" => BigDecimal.new(h["last"].to_s),
44
+ "high" => BigDecimal.new(h["high"].to_s),
45
+ "low" => BigDecimal.new(h["low"].to_s),
46
+ "volume" => BigDecimal.new(h["volume"].to_s),
47
+ "ltimestamp" => Time.now.to_i,
48
+ "vwap" => BigDecimal.new(h["vwap"].to_s)
49
+ }
50
+ end
51
+
52
+ # Get order book.
53
+ # @abstract
54
+ # @return [hash] array of market depth
55
+ # asks: [Array] 売りオーダー
56
+ # price : [BigDecimal]
57
+ # size : [BigDecimal]
58
+ # bids: [Array] 買いオーダー
59
+ # price : [BigDecimal]
60
+ # size : [BigDecimal]
61
+ # ltimestamp: [int] ローカルタイムスタンプ
62
+ def depth
63
+ h = get_ssl(@url_public + "/depth/btc_jpy")
64
+ {
65
+ "asks" => h["asks"].map{|a,b| [BigDecimal.new(a.to_s), BigDecimal.new(b.to_s)]}, # to_s でないと誤差が生じる
66
+ "bids" => h["bids"].map{|a,b| [BigDecimal.new(a.to_s), BigDecimal.new(b.to_s)]}, # to_s でないと誤差が生じる
67
+ "ltimestamp" => Time.now.to_i,
68
+ }
69
+ end
70
+
71
+ #############################################################
72
+ # API for private user data and trading
73
+ #############################################################
74
+
75
+ # Get account balance.
76
+ # @abstract
77
+ # @return [hash] account_balance_hash
78
+ # jpy: [hash]
79
+ # amount: [BigDecimal] 総日本円
80
+ # available: [BigDecimal] 取引可能な日本円
81
+ # btc [hash]
82
+ # amount: [BigDecimal] 総BTC
83
+ # available: [BigDecimal] 取引可能なBTC
84
+ # ltimestamp: [int] ローカルタイムスタンプ
85
+ def balance
86
+ have_key?
87
+ address = @url_private
88
+ body = { "method" => "get_info" }
89
+ h = post_ssl(address, body)
90
+ {
91
+ "jpy" => {
92
+ "amount" => BigDecimal.new(h["return"]["deposit"]["jpy"].to_s),
93
+ "available" => BigDecimal.new(h["return"]["funds"]["jpy"].to_s),
94
+ },
95
+ "btc" => {
96
+ "amount" => BigDecimal.new(h["return"]["deposit"]["btc"].to_s),
97
+ "available" => BigDecimal.new(h["return"]["funds"]["btc"].to_s),
98
+ },
99
+ "ltimestamp" => Time.now.to_i,
100
+ }
101
+ end
102
+
103
+ # Get open orders.
104
+ # @abstract
105
+ # @return [Array] open_orders_array
106
+ # @return [hash] history_order_hash
107
+ # success: [bool]
108
+ # id: [String] order id in the market
109
+ # rate: [BigDecimal]
110
+ # amount: [BigDecimal]
111
+ # order_type: [String] "sell" or "buy"
112
+ # order_status: [String] "active", "completed" or "canceled"
113
+ # ltimestamp: [int] Local Timestamp
114
+ def opens
115
+ have_key?
116
+ address = @url_private
117
+ body = { "method" => "active_orders" }
118
+ h = post_ssl(address, body)
119
+ h["return"].map{|key, value|
120
+ order_type = "buy" # when value["action"] is "bid"
121
+ order_type = "sell" if value["action"] == "ask"
122
+ {
123
+ "success" => "true",
124
+ "id" => key,
125
+ "rate" => BigDecimal.new(value["price"].to_s),
126
+ "amount" => BigDecimal.new(value["amount"].to_s),
127
+ "order_type" => order_type,
128
+ }
129
+ }
130
+ end
131
+
132
+ # Bought the amount of Bitcoin at the rate.
133
+ # 指数注文 買い.
134
+ # Abstract Method.
135
+ # @param [BigDecimal] rate
136
+ # @param [BigDecimal] amount
137
+ # @return [hash] history_order_hash
138
+ # success: [String] "true" or "false"
139
+ # id: [String] order id at the market
140
+ # rate: [BigDecimal] rate should be 5 multiples
141
+ # amount: [BigDecimal] minimal amount is 0.0001 BTC
142
+ # order_type: [String] "sell" or "buy"
143
+ # ltimestamp: [int] ローカルタイムスタンプ
144
+ def buy(rate, amount=BigDecimal.new(0))
145
+ have_key?
146
+ address = @url_private
147
+ rate = (rate.to_i / 5) * 5
148
+ body = {
149
+ "method" => "trade",
150
+ "currency_pair" => "btc_jpy",
151
+ "action" => "bid",
152
+ "price" => rate,
153
+ "amount" => amount.to_f.round(4)
154
+ }
155
+ h = post_ssl(address, body)
156
+ result = h["success"].to_i == 1 ? "true" : "false"
157
+ {
158
+ "success" => result,
159
+ "id" => h["return"]["order_id"].to_s,
160
+ "rate" => BigDecimal.new(rate.to_s),
161
+ "amount" => BigDecimal.new(amount.to_s),
162
+ "order_type" => "sell",
163
+ "ltimestamp" => Time.now.to_i,
164
+ }
165
+ end
166
+
167
+ # Buy the amount of Bitcoin from the market.
168
+ # 成行注文 買い.
169
+ # @abstract
170
+ # @param [BigDecimal] amount
171
+ # @return [hash] history_order_hash
172
+ # success: [bool]
173
+ # id: [String] order id in the market
174
+ # rate: [BigDecimal]
175
+ # amount: [BigDecimal]
176
+ # order_type: [String] "sell" or "buy"
177
+ # ltimestamp: [int] Local Timestamp
178
+ def market_buy(amount=BigDecimal.new("0.0"))
179
+ have_key?
180
+ address = @url_private
181
+ rate = (rate.to_i / 5) * 5
182
+ body = {
183
+ "method" => "trade",
184
+ "currency_pair" => "btc_jpy",
185
+ "action" => "bid",
186
+ "price" => 100000,
187
+ "amount" => amount.to_f.round(4)
188
+ }
189
+ h = post_ssl(address, body)
190
+ result = h["success"].to_i == 1 ? "true" : "false"
191
+ {
192
+ "success" => result,
193
+ "id" => h["return"]["order_id"].to_s,
194
+ "rate" => BigDecimal.new(rate.to_s),
195
+ "amount" => BigDecimal.new(amount.to_s),
196
+ "order_type" => "sell",
197
+ "ltimestamp" => Time.now.to_i,
198
+ }
199
+ end
200
+
201
+ # Sell the amount of Bitcoin at the rate.
202
+ # 指数注文 売り.
203
+ # Abstract Method.
204
+ # @param [BigDecimal] rate
205
+ # @param [BigDecimal] amount
206
+ # @return [hash] history_order_hash
207
+ # success: [String] "true" or "false"
208
+ # id: [String] order id at the market
209
+ # rate: [BigDecimal] rate should be 5 multiples
210
+ # amount: [BigDecimal] minimal amount is 0.0001 BTC
211
+ # order_type: [String] "sell" or "buy"
212
+ # ltimestamp: [int] ローカルタイムスタンプ
213
+ def sell(rate, amount=BigDecimal.new(0))
214
+ have_key?
215
+ address = @url_private
216
+ rate = (rate.to_i / 5) * 5
217
+ body = {
218
+ "method" => "trade",
219
+ "currency_pair" => "btc_jpy",
220
+ "action" => "ask",
221
+ "price" => rate,
222
+ "amount" => amount.to_f.round(4),
223
+ }
224
+ h = post_ssl(address, body)
225
+ result = h["success"].to_i == 1 ? "true" : "false"
226
+ {
227
+ "success" => result,
228
+ "id" => h["return"]["order_id"].to_s,
229
+ "rate" => BigDecimal.new(rate.to_s),
230
+ "amount" => BigDecimal.new(amount.to_s),
231
+ "order_type" => "sell",
232
+ "ltimestamp" => Time.now.to_i,
233
+ }
234
+ end
235
+
236
+ # Sell the amount of Bitcoin to the market.
237
+ # 成行注文 売り.
238
+ # @abstract
239
+ # @param [BigDecimal] amount
240
+ # @return [hash] history_order_hash
241
+ # success: [bool]
242
+ # id: [String] order id in the market
243
+ # rate: [BigDecimal]
244
+ # amount: [BigDecimal]
245
+ # order_type: [String] "sell" or "buy"
246
+ # ltimestamp: [int] Local Timestamp
247
+ def market_sell(amount=BigDecimal.new("0.0"))
248
+ have_key?
249
+ address = @url_private
250
+ rate = (rate.to_i / 5) * 5
251
+ body = {
252
+ "method" => "trade",
253
+ "currency_pair" => "btc_jpy",
254
+ "action" => "ask",
255
+ "price" => 5,
256
+ "amount" => amount.to_f.round(4),
257
+ }
258
+ h = post_ssl(address, body)
259
+ result = h["success"].to_i == 1 ? "true" : "false"
260
+ {
261
+ "success" => result,
262
+ "id" => h["return"]["order_id"].to_s,
263
+ "rate" => BigDecimal.new(rate.to_s),
264
+ "amount" => BigDecimal.new(amount.to_s),
265
+ "order_type" => "sell",
266
+ "ltimestamp" => Time.now.to_i,
267
+ }
268
+ end
269
+
270
+ # Cancel an open order
271
+ # @abstract
272
+ # @param [int or string] order id
273
+ # @return [hash]
274
+ # success: [bool] status
275
+ def cancel(id)
276
+ have_key?
277
+ address = @url_private
278
+ body = {
279
+ "method" => "cancel_order",
280
+ "currency_pair" => "btc_jpy",
281
+ "order_id" => id,
282
+ }
283
+ h = post_ssl(address, body)
284
+ {
285
+ "success" => h["success"]==1,
286
+ }
287
+ end
288
+
289
+ # Cancel all open orders
290
+ # @abstract
291
+ # @return [array]
292
+ # success: [bool] status
293
+ def cancel_all
294
+ opens.each{|h|
295
+ print cancel(h["id"])
296
+ }
297
+ end
298
+
299
+ private
300
+
301
+ def initialize_https(uri)
302
+ https = Net::HTTP.new(uri.host, uri.port)
303
+ https.use_ssl = true
304
+ https.open_timeout = 5
305
+ https.read_timeout = 15
306
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
307
+ https.verify_depth = 5
308
+ https
309
+ end
310
+
311
+ # Connect to address via https, and return json response.
312
+ def get_ssl(address)
313
+ uri = URI.parse(address)
314
+
315
+ begin
316
+ https = initialize_https(uri)
317
+ https.start {|w|
318
+ response = w.get(uri.request_uri)
319
+ case response
320
+ when Net::HTTPSuccess
321
+ json = JSON.parse(response.body)
322
+ raise JSONException, response.body if json == nil
323
+ return json
324
+ else
325
+ raise ConnectionFailedException, "Failed to connect to #{@name}."
326
+ end
327
+ }
328
+ rescue
329
+ raise
330
+ end
331
+ end
332
+
333
+ def get_nonce
334
+ pre_nonce = @@nonce
335
+ next_nonce = Time.now.to_f
336
+
337
+ if next_nonce <= pre_nonce
338
+ @@nonce = pre_nonce + 1
339
+ else
340
+ @@nonce = next_nonce
341
+ end
342
+
343
+ return @@nonce
344
+ end
345
+
346
+ def get_sign(req)
347
+ secret = @api_secret
348
+ text = req.body
349
+
350
+ OpenSSL::HMAC::hexdigest(OpenSSL::Digest.new('sha512'), secret, text)
351
+ end
352
+
353
+ # Connect to address via https, and return json response.
354
+ def post_ssl(address, data={})
355
+ uri = URI.parse(address)
356
+ data["nonce"] = get_nonce
357
+
358
+ begin
359
+ req = Net::HTTP::Post.new(uri)
360
+ req.set_form_data(data)
361
+ req["Key"] = @api_key
362
+ req["Sign"] = get_sign(req)
363
+
364
+ https = initialize_https(uri)
365
+ https.start {|w|
366
+ response = w.request(req)
367
+ case response
368
+ when Net::HTTPSuccess
369
+ json = JSON.parse(response.body)
370
+ raise JSONException, response.body if json == nil
371
+ return json
372
+ else
373
+ raise ConnectionFailedException, "Failed to connect to #{@name}: " + response.value
374
+ end
375
+ }
376
+ rescue
377
+ raise
378
+ end
379
+ end
380
+
381
+ end
382
+ end