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,156 @@
1
+ require_relative 'market.rb'
2
+ require 'net/http'
3
+ require 'openssl'
4
+ require 'json'
5
+ require 'bigdecimal'
6
+
7
+ module Kaesen
8
+ # Kraken Wrapper Class
9
+ # https://www.kraken.com/help/api
10
+
11
+ class Kraken < Market
12
+ @@nonce = 0
13
+
14
+ def initialize()
15
+ super()
16
+ @name = "Kraken"
17
+ @api_key = ENV["KRAKEN_KEY"]
18
+ @api_secret = ENV["KRAKEN_SECRET"]
19
+ @url_public = "https://api.kraken.com/0/public"
20
+ @url_private = "https://api.kraken.com/0/private"
21
+ end
22
+
23
+ #############################################################
24
+ # API for public information
25
+ #############################################################
26
+
27
+ # Get ticker information.
28
+ # @return [hash] ticker
29
+ # ask: [BigDecimal] 最良売気配値
30
+ # bid: [BigDecimal] 最良買気配値
31
+ # last: [BigDecimal] 最近値(?用語要チェック), last price
32
+ # high: [BigDecimal] 高値
33
+ # low: [BigDecimal] 安値
34
+ # volume: [BigDecimal] 取引量
35
+ # ltimestamp: [int] ローカルタイムスタンプ
36
+ # vwap: [BigDecimal] 過去24時間の加重平均
37
+ def ticker
38
+ h = get_ssl(@url_public + "/Ticker?pair=XXBTZJPY") # cf. XBTJPY is alias of XXBTZJPY
39
+ h = h["XXBTZJPY"]
40
+ {
41
+ "ask" => BigDecimal.new(h["a"][0]),
42
+ "bid" => BigDecimal.new(h["b"][0]),
43
+ "last" => BigDecimal.new(h["c"][0]),
44
+ "high" => BigDecimal.new(h["h"][1]), # of the previous 24 hours
45
+ "low" => BigDecimal.new(h["l"][1]), # of the previous 24 hours
46
+ "volume" => BigDecimal.new(h["v"][1]), # of the previous 24 hours
47
+ "ltimestamp" => Time.now.to_i,
48
+ "vwap" => BigDecimal.new(h["p"][1]), # of the previous 24 hours
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?pair=XXBTZJPY")
64
+ h = h["XXBTZJPY"]
65
+ {
66
+ "asks" => h["asks"].map{|a,b,t| [BigDecimal.new(a.to_s), BigDecimal.new(b.to_s)]}, # to_s でないと誤差が生じる
67
+ "bids" => h["bids"].map{|a,b,t| [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
+ raise APIErrorException, json["error"] unless json["error"].empty?
97
+ return json["result"]
98
+ else
99
+ raise ConnectionFailedException, "Failed to connect to #{@name}."
100
+ end
101
+ }
102
+ rescue
103
+ raise
104
+ end
105
+ end
106
+
107
+ def get_nonce
108
+ pre_nonce = @@nonce
109
+ next_nonce = (Time.now.to_i) * 100
110
+
111
+ if next_nonce <= pre_nonce
112
+ @@nonce = pre_nonce + 1
113
+ else
114
+ @@nonce = next_nonce
115
+ end
116
+
117
+ return @@nonce
118
+ end
119
+
120
+ def get_sign(req)
121
+ secret = @api_secret
122
+ text = req.body
123
+
124
+ OpenSSL::HMAC::hexdigest(OpenSSL::Digest.new('sha512'), secret, text)
125
+ end
126
+
127
+ # Connect to address via https, and return json response.
128
+ def post_ssl(address, data={})
129
+ uri = URI.parse(address)
130
+ data["nonce"] = get_nonce
131
+
132
+ begin
133
+ req = Net::HTTP::Post.new(uri)
134
+ req.set_form_data(data)
135
+ req["Key"] = @api_key
136
+ req["Sign"] = get_sign(req)
137
+
138
+ https = initialize_https(uri)
139
+ https.start {|w|
140
+ response = w.request(req)
141
+ case response
142
+ when Net::HTTPSuccess
143
+ json = JSON.parse(response.body)
144
+ raise JSONException, response.body if json == nil
145
+ return json
146
+ else
147
+ raise ConnectionFailedException, "Failed to connect to #{@name}: " + response.value
148
+ end
149
+ }
150
+ rescue
151
+ raise
152
+ end
153
+ end
154
+
155
+ end
156
+ end
@@ -0,0 +1,154 @@
1
+ require_relative 'market.rb'
2
+ require 'net/http'
3
+ require 'openssl'
4
+ require 'json'
5
+ require 'bigdecimal'
6
+
7
+ module Kaesen
8
+ # Lakebtc Wrapper Class
9
+ # https://www.lakebtc.com/s/api
10
+
11
+ class Lakebtc < Market
12
+ @@nonce = 0
13
+
14
+ def initialize()
15
+ super()
16
+ @name = "Lakebtc"
17
+ @api_key = ENV["LAKEBTC_KEY"]
18
+ @api_secret = ENV["LAKEBTC_SECRET"]
19
+ @url_public = "https://www.LakeBTC.com/api_v1"
20
+ @url_private = @url_public
21
+ end
22
+
23
+ #############################################################
24
+ # API for public information
25
+ #############################################################
26
+
27
+ # Get ticker information.
28
+ # @return [hash] ticker
29
+ # ask: [BigDecimal] 最良売気配値
30
+ # bid: [BigDecimal] 最良買気配値
31
+ # last: [BigDecimal] 最近値(?用語要チェック), last price
32
+ # high: [BigDecimal] 高値
33
+ # low: [BigDecimal] 安値
34
+ # volume: [BigDecimal] 取引量
35
+ # ltimestamp: [int] ローカルタイムスタンプ
36
+ # vwap: [BigDecimal] 過去24時間の加重平均
37
+ def ticker
38
+ h = get_ssl(@url_public + "/ticker") # the id of BTCJPY is 5.
39
+ h = h["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"
45
+ # "low"
46
+ # "volume"
47
+ "ltimestamp" => Time.now.to_i,
48
+ # "vwap"
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 + "/bcorderbook?symbol=btcjpy") # the id of BTCJPY is 5.
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
+ private
72
+
73
+ def initialize_https(uri)
74
+ https = Net::HTTP.new(uri.host, uri.port)
75
+ https.use_ssl = true
76
+ https.open_timeout = 5
77
+ https.read_timeout = 15
78
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
79
+ https.verify_depth = 5
80
+ https
81
+ end
82
+
83
+ # Connect to address via https, and return json response.
84
+ def get_ssl(address)
85
+ uri = URI.parse(address)
86
+
87
+ begin
88
+ https = initialize_https(uri)
89
+ https.start {|w|
90
+ response = w.get(uri.request_uri)
91
+ case response
92
+ when Net::HTTPSuccess
93
+ json = JSON.parse(response.body)
94
+ raise JSONException, response.body if json == nil
95
+ return json
96
+ else
97
+ raise ConnectionFailedException, "Failed to connect to #{@name}."
98
+ end
99
+ }
100
+ rescue
101
+ raise
102
+ end
103
+ end
104
+
105
+ def get_nonce
106
+ pre_nonce = @@nonce
107
+ next_nonce = (Time.now.to_i) * 100
108
+
109
+ if next_nonce <= pre_nonce
110
+ @@nonce = pre_nonce + 1
111
+ else
112
+ @@nonce = next_nonce
113
+ end
114
+
115
+ return @@nonce
116
+ end
117
+
118
+ def get_sign(req)
119
+ secret = @api_secret
120
+ text = req.body
121
+
122
+ OpenSSL::HMAC::hexdigest(OpenSSL::Digest.new('sha512'), secret, text)
123
+ end
124
+
125
+ # Connect to address via https, and return json response.
126
+ def post_ssl(address, data={})
127
+ uri = URI.parse(address)
128
+ data["nonce"] = get_nonce
129
+
130
+ begin
131
+ req = Net::HTTP::Post.new(uri)
132
+ req.set_form_data(data)
133
+ req["Key"] = @api_key
134
+ req["Sign"] = get_sign(req)
135
+
136
+ https = initialize_https(uri)
137
+ https.start {|w|
138
+ response = w.request(req)
139
+ case response
140
+ when Net::HTTPSuccess
141
+ json = JSON.parse(response.body)
142
+ raise JSONException, response.body if json == nil
143
+ return json
144
+ else
145
+ raise ConnectionFailedException, "Failed to connect to #{@name}: " + response.value
146
+ end
147
+ }
148
+ rescue
149
+ raise
150
+ end
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,188 @@
1
+ module Kaesen
2
+
3
+ # Exchange markets.
4
+ # @abstract
5
+ class Market
6
+ attr_reader :name
7
+
8
+ def initialize
9
+ @name = nil # [String] name of exchange market
10
+ @api_key = nil # [String]
11
+ @api_secret = nil # [String]
12
+ @url_public = nil # [String]
13
+ @url_private = nil # [String]
14
+ end
15
+
16
+ #############################################################
17
+ # API for public information
18
+ #############################################################
19
+
20
+ # Get ticker information.
21
+ # @abstract
22
+ # @return [hash] ticker
23
+ # ask: [BigDecimal] 最良売気配値
24
+ # bid: [BigDecimal] 最良買気配値
25
+ # last: [BigDecimal] 最近値(?用語要チェック), last price
26
+ # high: [BigDecimal] 高値
27
+ # low: [BigDecimal] 安値
28
+ # volume: [BigDecimal] 取引量
29
+ # ltimestamp: [int] Local Timestamp
30
+ def ticker
31
+ raise NotImplemented.new()
32
+ end
33
+
34
+ # Get order book.
35
+ # @abstract
36
+ # @return [hash] array of market depth
37
+ # asks: [Array] 売りオーダー
38
+ # price : [BigDecimal]
39
+ # size : [BigDecimal]
40
+ # bids: [Array] 買いオーダー
41
+ # price : [BigDecimal]
42
+ # size : [BigDecimal]
43
+ # ltimestamp: [int] Local Timestamp
44
+ def depth
45
+ raise NotImplemented.new()
46
+ end
47
+
48
+ #############################################################
49
+ # API for private user data and trading
50
+ #############################################################
51
+
52
+ # Get account balance.
53
+ # @abstract
54
+ # @return [hash] account_balance_hash
55
+ # jpy: [hash]
56
+ # amount: [BigDecimal] 総日本円
57
+ # available: [BigDecimal] 取引可能な日本円
58
+ # btc [hash]
59
+ # amount: [BigDecimal] 総BTC
60
+ # available: [BigDecimal] 取引可能なBTC
61
+ # ltimestamp: [int] Local Timestamp
62
+ def balance
63
+ raise NotImplemented.new()
64
+ end
65
+
66
+ # Get open orders.
67
+ # @abstract
68
+ # @return [Array] open_orders_array
69
+ # @return [hash] history_order_hash
70
+ # success: [bool]
71
+ # id: [String] order id in the market
72
+ # rate: [BigDecimal]
73
+ # amount: [BigDecimal]
74
+ # order_type: [String] "sell" or "buy"
75
+ # ltimestamp: [int] Local Timestamp
76
+ def opens
77
+ raise NotImplemented.new()
78
+ end
79
+
80
+ # Buy the amount of Bitcoin at the rate.
81
+ # 指数注文 買い.
82
+ # @abstract
83
+ # @param [BigDecimal] rate
84
+ # @param [BigDecimal] amount
85
+ # @return [hash] history_order_hash
86
+ # success: [bool]
87
+ # id: [String] order id in the market
88
+ # rate: [BigDecimal]
89
+ # amount: [BigDecimal]
90
+ # order_type: [String] "sell" or "buy"
91
+ # ltimestamp: [int] Local Timestamp
92
+ def buy(rate, amount=BigDecimal.new("0.0"))
93
+ raise NotImplemented.new()
94
+ end
95
+
96
+ # Buy the amount of Bitcoin from the market.
97
+ # 成行注文 買い.
98
+ # @abstract
99
+ # @param [BigDecimal] amount
100
+ # @return [hash] history_order_hash
101
+ # success: [bool]
102
+ # id: [String] order id in the market
103
+ # rate: [BigDecimal]
104
+ # amount: [BigDecimal]
105
+ # order_type: [String] "sell" or "buy"
106
+ # ltimestamp: [int] Local Timestamp
107
+ def market_buy(amount=BigDecimal.new("0.0"))
108
+ raise NotImplemented.new()
109
+ end
110
+
111
+ # Sell the amount of Bitcoin at the rate.
112
+ # 指数注文 売り.
113
+ # @abstract
114
+ # @param [BigDecimal] rate
115
+ # @param [BigDecimal] amount
116
+ # @return [hash] history_order_hash
117
+ # success: [String] "true" or "false"
118
+ # id: [String] order id in the market
119
+ # rate: [BigDecimal]
120
+ # amount: [BigDecimal]
121
+ # order_type: [String] "sell" or "buy"
122
+ # ltimestamp: [int] Local Timestamp
123
+ def sell(rate, amount=BigDecimal.new("0.0"))
124
+ raise NotImplemented.new()
125
+ end
126
+
127
+ # Sell the amount of Bitcoin to the market.
128
+ # 成行注文 売り.
129
+ # @abstract
130
+ # @param [BigDecimal] amount
131
+ # @return [hash] history_order_hash
132
+ # success: [bool]
133
+ # id: [String] order id in the market
134
+ # rate: [BigDecimal]
135
+ # amount: [BigDecimal]
136
+ # order_type: [String] "sell" or "buy"
137
+ # ltimestamp: [int] Local Timestamp
138
+ def market_sell(amount=BigDecimal.new("0.0"))
139
+ raise NotImplemented.new()
140
+ end
141
+
142
+ # Cancel an open order
143
+ # @abstract
144
+ # @param [int or string] order id
145
+ # @return [hash]
146
+ # success: [bool] status
147
+ def cancel(id)
148
+ raise NotImplemented.new()
149
+ end
150
+
151
+ # Cancel all open orders
152
+ # @abstract
153
+ # @return [array]
154
+ # success: [bool] status
155
+ def cancel_all
156
+ raise NotImplemented.new()
157
+ end
158
+
159
+ # Pretty printer for data including BigDecimal
160
+ # @param [any] data that may include BigDecimal
161
+ # @return [any] data that does not include BigDecimal
162
+ def self.unBigDecimal(x)
163
+ if x.is_a?(Array)
164
+ x.map{|y| unBigDecimal(y)}
165
+ elsif x.is_a?(Hash)
166
+ x.map{|k,v|
167
+ [k, unBigDecimal(v)]
168
+ }.to_h
169
+ elsif x.is_a?(BigDecimal)
170
+ x.to_f
171
+ else
172
+ x
173
+ end
174
+ end
175
+
176
+ private
177
+
178
+ # Check the API key and API secret key.
179
+ def have_key?
180
+ raise "Your #{@name} API key is not set" if @api_key.nil?
181
+ raise "Your #{@name} API secret is not set" if @api_secret.nil?
182
+ end
183
+
184
+ class ConnectionFailedException < StandardError; end
185
+ class APIErrorException < StandardError; end
186
+ class JSONException < StandardError; end
187
+ end
188
+ end