kraken_ruby 0.4.1 → 0.4.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af88f54624c729cd704d10d7e489ab9a05c8d9c7
4
+ data.tar.gz: 8f476f022892acf97687f39b6342ce001baf4fb9
5
+ SHA512:
6
+ metadata.gz: 0a9e886717cbdee05f41b87bafc21c4d3e94616ac5195c1d4991672a8343d627876afeb10abd11def56212baed99462cb6a353a53a3f802554e36f43a6fa2b29
7
+ data.tar.gz: 27bb01c561250b27a2c0416192cf4440b2e54e7e9fd6276ba13b9865959fa4f2c52c6aba2a881830f00374059bf266c9576d7506e1a7d24d28e72eeac2929f65
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kraken_ruby.gemspec
4
+ gemspec
5
+ gem 'httparty'
6
+ gem 'hashie'
7
+ gem 'addressable'
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ kraken_ruby (0.4.1)
5
+ addressable
6
+ hashie
7
+ httparty
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ addressable (2.3.5)
13
+ diff-lcs (1.2.5)
14
+ hashie (2.0.5)
15
+ httparty (0.12.0)
16
+ json (~> 1.8)
17
+ multi_xml (>= 0.5.2)
18
+ json (1.8.1)
19
+ multi_xml (0.5.5)
20
+ rake (10.1.1)
21
+ rspec (2.14.1)
22
+ rspec-core (~> 2.14.0)
23
+ rspec-expectations (~> 2.14.0)
24
+ rspec-mocks (~> 2.14.0)
25
+ rspec-core (2.14.7)
26
+ rspec-expectations (2.14.5)
27
+ diff-lcs (>= 1.1.3, < 2.0)
28
+ rspec-mocks (2.14.6)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ addressable
35
+ bundler (~> 1.3)
36
+ hashie
37
+ httparty
38
+ kraken_ruby!
39
+ rake
40
+ rspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Alexander Leishman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # KrakenRuby
2
+
3
+ ### IMPORTANT
4
+
5
+ This gem is currently built to only query data. There is an experimental ```add_order``` trading method that can be used at **your own risk**. Please thoroughly vet everything in the code for yourself before using this gem to buy, sell, or move any of your assets.
6
+
7
+ PLEASE submit an issue or pull request if you notice any bugs, security holes, or potential improvements.
8
+
9
+ ### Description
10
+
11
+ This gem is a wrapper for the [Kraken Digital Asset Trading Platform](https://www.kraken.com) API. Official documentation from Kraken can be found [here](https://www.kraken.com/help/api).
12
+
13
+ The current version (0.2.0) can be used to query public and private market data. Private data queries require use of your account API keys.
14
+
15
+ ### Pending Future Updates
16
+
17
+ - Solid trade execution functionality
18
+ - More comprehensive test suite for methods requiring authentication (using VCR perhaps)
19
+ - More comprehensive documentation
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'kraken_ruby'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install kraken_ruby
34
+
35
+ ## Usage
36
+
37
+ Create a Kraken client:
38
+
39
+ ```ruby
40
+ API_KEY = '3bH+M/nLp......'
41
+ API_SECRET = 'wQG+7Lr9b.....'
42
+
43
+ kraken = Kraken::Client.new(API_KEY, API_SECRET)
44
+
45
+ time = kraken.server_time
46
+ time.unixtime #=> 1393056191
47
+ ```
48
+
49
+ ### Public Data Methods
50
+
51
+ #### Server Time
52
+
53
+ This functionality is provided by Kraken to to aid in approximating the skew time between the server and client.
54
+
55
+ ```ruby
56
+ time = kraken.server_time
57
+
58
+ time.unixtime #=> 1393056191
59
+ time.rfc1123 #=> "Sat, 22 Feb 2014 08:28:04 GMT"
60
+ ```
61
+
62
+ #### Asset Info
63
+
64
+ Returns the assets that can be traded on the exchange. This method can be passed ```info```, ```aclass``` (asset class), and ```asset``` options. An example below is given for each:
65
+
66
+ ```ruby
67
+ kraken.assets
68
+ ```
69
+
70
+ #### Asset Pairs
71
+
72
+ ```ruby
73
+ pairs = kraken.asset_pairs
74
+ ```
75
+
76
+ #### Ticker Information
77
+
78
+ ```ruby
79
+ ticker_data = kraken.ticker('XLTCXXDG, ZUSDXXVN')
80
+ ```
81
+
82
+ #### Order Book
83
+
84
+ Get market depth information for given asset pairs
85
+
86
+ ```ruby
87
+ depth_data = kraken.order_book('LTCXRP')
88
+ ```
89
+
90
+ #### Trades
91
+
92
+ Get recent trades
93
+
94
+ ```ruby
95
+ trades = kraken.trades('LTCXRP')
96
+ ```
97
+
98
+ #### Spread
99
+
100
+ Get spread data for a given asset pair
101
+
102
+ ```ruby
103
+ spread = kraken.spread('LTCXRP')
104
+ ```
105
+
106
+ ### Private Data Methods
107
+
108
+ #### Balance
109
+
110
+ Get account balance for each asset
111
+ Note: Rates used for the floating valuation is the midpoint of the best bid and ask prices
112
+
113
+ ```ruby
114
+ balance = kraken.balance
115
+ ```
116
+
117
+ #### Trade Balance
118
+
119
+ Get account trade balance
120
+
121
+ ```ruby
122
+ trade_balance = kraken.trade_balance
123
+ ```
124
+
125
+ #### Open Orders
126
+
127
+ ```ruby
128
+ open_orders = kraken.open_orders
129
+ ```
130
+
131
+ #### Query Orders
132
+
133
+ See all orders
134
+
135
+ ```ruby
136
+ orders = kraken.query_orders
137
+ ```
138
+
139
+ #### Trades History
140
+
141
+ Get array of all trades
142
+
143
+ ```ruby
144
+ trades = kraken.trade_history
145
+ ```
146
+
147
+ #### Query Trades
148
+
149
+ **Input:** Comma delimited list of transaction (tx) ids
150
+
151
+ ```ruby
152
+ trades = kraken.query_trades(tx_ids)
153
+ ```
154
+
155
+ #### Open Positions
156
+
157
+ **Input:** Comma delimited list of transaction (tx) ids
158
+
159
+ ```ruby
160
+ positions = kraken.open_positions(tx_ids)
161
+ ```
162
+
163
+ #### Ledgers Info
164
+
165
+ ```ruby
166
+ ledgers = kraken.ledgers_info
167
+ ```
168
+
169
+ #### Ledgers Info
170
+
171
+ **Input:** Comma delimited list of ledger ids
172
+
173
+ ```ruby
174
+ ledgers = kraken.query_ledgers(ledger_ids)
175
+ ```
176
+
177
+ #### Trade Volume
178
+
179
+ **Input:** Comma delimited list of asset pairs
180
+
181
+ ```ruby
182
+ asset_pairs = 'XLTCXXDG, ZEURXXDG'
183
+ volume = kraken.query_ledgers(asset_pairs)
184
+ ```
185
+
186
+ ### Adding and Cancelling Orders
187
+
188
+ #### Add Order
189
+
190
+ There are 4 required parameters for buying an order. The example below illustrates the most basic order. Please see the [Kraken documentation](https://www.kraken.com/help/api#add-standard-order) for the parameters required for more advanced order types.
191
+ ```ruby
192
+ # buying 0.01 XBT (bitcoin) for XRP (ripple) at market price
193
+ opts = {
194
+ pair: 'XBTXRP',
195
+ type: 'buy',
196
+ ordertype: 'market',
197
+ volume: 0.01
198
+ }
199
+
200
+ kraken.add_order(opts)
201
+
202
+ ```
203
+
204
+ #### Cancel Order
205
+
206
+ ```ruby
207
+ kraken.cancel_order("UKIYSP-9VN27-AJWWYC")
208
+ ```
209
+
210
+ ## Contributing
211
+
212
+ 1. Fork it
213
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
214
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
215
+ 4. Push to the branch (`git push origin my-new-feature`)
216
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kraken_ruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "kraken_ruby"
8
+ spec.version = KrakenRuby::VERSION
9
+ spec.authors = ["Alexander Leishman"]
10
+ spec.email = ["leishman3@gmail.com"]
11
+ spec.description = %q{"Wrapper for Kraken Exchange API"}
12
+ spec.summary = %q{"Wrapper for Kraken Exchange API"}
13
+ spec.homepage = "https://www.kraken.com/help/api"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "httparty"
26
+ spec.add_dependency "hashie"
27
+ spec.add_dependency "addressable"
28
+ end
@@ -0,0 +1,2 @@
1
+ require "kraken_ruby/version"
2
+ require "kraken_ruby/client"
@@ -0,0 +1,181 @@
1
+ require 'base64'
2
+ require 'securerandom'
3
+ require 'addressable/uri'
4
+ require 'httparty'
5
+ require 'hashie'
6
+
7
+ module Kraken
8
+ class Client
9
+ include HTTParty
10
+
11
+ def initialize(api_key=nil, api_secret=nil, options={})
12
+ @api_key = api_key
13
+ @api_secret = api_secret
14
+ @api_version = options[:version] ||= '0'
15
+ @base_uri = options[:base_uri] ||= 'https://api.kraken.com'
16
+ end
17
+
18
+ ###########################
19
+ ###### Public Data ########
20
+ ###########################
21
+
22
+ def server_time
23
+ get_public 'Time'
24
+ end
25
+
26
+ def assets(opts={})
27
+ get_public 'Assets'
28
+ end
29
+
30
+ def asset_pairs(opts={})
31
+ get_public 'AssetPairs', opts
32
+ end
33
+
34
+ def ticker(pairs) # takes string of comma delimited pairs
35
+ opts = { 'pair' => pairs }
36
+ get_public 'Ticker', opts
37
+ end
38
+
39
+ def order_book(pair, opts={})
40
+ opts['pair'] = pair
41
+ get_public 'Depth', opts
42
+ end
43
+
44
+ def trades(pair, opts={})
45
+ opts['pair'] = pair
46
+ get_public 'Trades', opts
47
+ end
48
+
49
+ def spread(pair, opts={})
50
+ opts['pair'] = pair
51
+ get_public 'Spread', opts
52
+ end
53
+
54
+ def get_public(method, opts={})
55
+ url = @base_uri + '/' + @api_version + '/public/' + method
56
+ r = self.class.get(url, query: opts)
57
+ hash = Hashie::Mash.new(JSON.parse(r.body))
58
+ hash[:result]
59
+ end
60
+
61
+ ######################
62
+ ##### Private Data ###
63
+ ######################
64
+
65
+ def balance(opts={})
66
+ post_private 'Balance', opts
67
+ end
68
+
69
+ def trade_balance(opts={})
70
+ post_private 'TradeBalance', opts
71
+ end
72
+
73
+ def open_orders(opts={})
74
+ post_private 'OpenOrders', opts
75
+ end
76
+
77
+ def query_orders(opts={})
78
+ post_private 'QueryOrders', opts
79
+ end
80
+
81
+ def trade_history(opts={})
82
+ post_private 'TradesHistory', opts
83
+ end
84
+
85
+ def query_trades(tx_ids, opts={})
86
+ opts['txid'] = tx_ids
87
+ post_private 'QueryTrades', opts
88
+ end
89
+
90
+ def open_positions(tx_ids, opts={})
91
+ opts['txid'] = tx_ids
92
+ post_private 'OpenPositions', opts
93
+ end
94
+
95
+ def ledgers_info(opts={})
96
+ post_private 'Ledgers', opts
97
+ end
98
+
99
+ def query_ledgers(ledger_ids, opts={})
100
+ opts['id'] = ledger_ids
101
+ post_private 'QueryLedgers', opts
102
+ end
103
+
104
+ def trade_volume(asset_pairs)
105
+ opts['pair'] = asset_pairs
106
+ post_private 'TradeVolume', opts
107
+ end
108
+
109
+ #### Private User Trading ####
110
+
111
+ def add_order(opts={})
112
+ required_opts = %w{ pair type ordertype volume }
113
+ leftover = required_opts - opts.keys.map(&:to_s)
114
+ if leftover.length > 0
115
+ raise ArgumentError.new("Required options, not given. Input must include #{leftover}")
116
+ end
117
+ post_private 'AddOrder', opts
118
+ end
119
+
120
+ def cancel_order(txid)
121
+ opts = { txid: txid }
122
+ post_private 'CancelOrder', opts
123
+ end
124
+
125
+ #######################
126
+ #### Generate Signed ##
127
+ ##### Post Request ####
128
+ #######################
129
+
130
+ private
131
+
132
+ def post_private(method, opts={})
133
+ opts['nonce'] = nonce
134
+ post_data = encode_options(opts)
135
+
136
+ headers = {
137
+ 'API-Key' => @api_key,
138
+ 'API-Sign' => generate_signature(method, post_data, opts)
139
+ }
140
+
141
+ url = @base_uri + url_path(method)
142
+ r = self.class.post(url, { headers: headers, body: post_data }).parsed_response
143
+ r['error'].empty? ? r['result'] : r['error']
144
+ end
145
+
146
+ # Generate a 64-bit nonce where the 32 high bits come directly from the current
147
+ # timestamp and the low 32 bits are pseudorandom. We can't use a pure [P]RNG here
148
+ # because the Kraken API requires every request within a given session to use a
149
+ # monotonically increasing nonce value. This approach splits the difference.
150
+ def nonce
151
+ high_bits = Time.now.to_i << 32
152
+ low_bits = SecureRandom.random_number(2 ** 32) & 0xffffffff
153
+ (high_bits | low_bits).to_s
154
+ end
155
+
156
+ def encode_options(opts)
157
+ uri = Addressable::URI.new
158
+ uri.query_values = opts
159
+ uri.query
160
+ end
161
+
162
+ def generate_signature(method, post_data, opts={})
163
+ key = Base64.decode64(@api_secret)
164
+ message = generate_message(method, opts, post_data)
165
+ generate_hmac(key, message)
166
+ end
167
+
168
+ def generate_message(method, opts, data)
169
+ digest = OpenSSL::Digest.new('sha256', opts['nonce'] + data).digest
170
+ url_path(method) + digest
171
+ end
172
+
173
+ def generate_hmac(key, message)
174
+ Base64.strict_encode64(OpenSSL::HMAC.digest('sha512', key, message))
175
+ end
176
+
177
+ def url_path(method)
178
+ '/' + @api_version + '/private/' + method
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,3 @@
1
+ module KrakenRuby
2
+ VERSION = "0.4.2"
3
+ end
@@ -0,0 +1,68 @@
1
+ require 'kraken_ruby'
2
+
3
+ describe Kraken::Client do
4
+
5
+ # YOU MUST SET ENVIRONMENT VARIABLES KRAKEN_API_KEY AND
6
+ # KRAKEN_API_SECRET TO TEST PRIVATE DATA QUERIES. PRIVATE
7
+ # TESTS WILL FAIL OTHERWISE.
8
+
9
+ API_KEY = ENV['KRAKEN_API_KEY']
10
+ API_SECRET = ENV['KRAKEN_API_SECRET']
11
+
12
+ before :each do
13
+ sleep 0.3 # to prevent rapidly pinging the Kraken server
14
+ end
15
+
16
+ let(:kraken){Kraken::Client.new(API_KEY, API_SECRET)}
17
+
18
+ context "public data" do
19
+ it "gets the proper server time" do
20
+ kraken_time = DateTime.parse(kraken.server_time.rfc1123)
21
+ utc_time = Time.now.getutc
22
+ expect(kraken_time.day).to eq utc_time.day
23
+ expect(kraken_time.hour).to eq utc_time.hour
24
+ end
25
+
26
+ it "gets list of tradeable assets" do
27
+ expect(kraken.assets).to respond_to :XLTC
28
+ end
29
+
30
+ it "gets list of asset pairs" do
31
+ expect(kraken.asset_pairs).to respond_to :XLTCXXDG
32
+ end
33
+
34
+ it "gets public ticker data for given asset pairs" do
35
+ result = kraken.ticker('XLTCXXDG, ZEURXXDG')
36
+ expect(result).to respond_to :XLTCXXDG
37
+ expect(result).to respond_to :ZEURXXDG
38
+ end
39
+
40
+ it "gets order book data for a given asset pair" do
41
+ order_book = kraken.order_book('XLTCXXDG')
42
+ expect(order_book.XLTCXXDG).to respond_to :asks
43
+ end
44
+
45
+ it "gets an array of trades data for a given asset pair" do
46
+ trades = kraken.trades('XLTCXXDG')
47
+ expect(trades.XLTCXXDG).to be_instance_of(Array)
48
+ end
49
+
50
+ it "gets an array of spread data for a given asset pair" do
51
+ spread = kraken.spread('XLTCXXDG')
52
+ expect(spread.XLTCXXDG).to be_instance_of(Array)
53
+ end
54
+ end
55
+
56
+ context "private data" do # More tests to come
57
+ it "gets the user's balance" do
58
+ expect(kraken.balance).to be_instance_of(Hash)
59
+ end
60
+
61
+ it "uses a 64 bit nonce" do
62
+ nonce = kraken.send :nonce
63
+ expect(nonce.to_i.size).to eq(8)
64
+ expect(nonce.to_i).to be_instance_of(Bignum)
65
+ end
66
+ end
67
+
68
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kraken_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Leishman