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.
- checksums.yaml +7 -0
- data/.env.sample +15 -0
- data/.gitignore +44 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/kaesen.gemspec +34 -0
- data/lib/kaesen.rb +17 -0
- data/lib/kaesen/bitflyer.rb +390 -0
- data/lib/kaesen/bitflyerfx.rb +26 -0
- data/lib/kaesen/btcbox.rb +294 -0
- data/lib/kaesen/client.rb +65 -0
- data/lib/kaesen/coincheck.rb +371 -0
- data/lib/kaesen/kraken.rb +156 -0
- data/lib/kaesen/lakebtc.rb +154 -0
- data/lib/kaesen/market.rb +188 -0
- data/lib/kaesen/monetago.rb +110 -0
- data/lib/kaesen/quoine.rb +155 -0
- data/lib/kaesen/version.rb +3 -0
- data/lib/kaesen/zaif.rb +382 -0
- metadata +129 -0
@@ -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
|
data/lib/kaesen/zaif.rb
ADDED
@@ -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
|