itbit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.txt +22 -0
- data/README.md +146 -0
- data/Rakefile +1 -0
- data/itbit.gemspec +33 -0
- data/lib/itbit.rb +16 -0
- data/lib/itbit/api.rb +33 -0
- data/lib/itbit/market.rb +70 -0
- data/lib/itbit/order.rb +113 -0
- data/lib/itbit/trade.rb +55 -0
- data/lib/itbit/version.rb +3 -0
- data/lib/itbit/wallet.rb +30 -0
- data/spec/fixtures/market_ticker_xbteur.json +1 -0
- data/spec/fixtures/market_ticker_xbtsgd.json +1 -0
- data/spec/fixtures/market_ticker_xbtusd.json +1 -0
- data/spec/fixtures/open_order.json +1 -0
- data/spec/fixtures/order.json +1 -0
- data/spec/fixtures/order_book.json +1 -0
- data/spec/fixtures/orders.json +1 -0
- data/spec/fixtures/trades.json +1 -0
- data/spec/fixtures/user_trades.json +1 -0
- data/spec/fixtures/wallet.json +1 -0
- data/spec/fixtures/wallets.json +1 -0
- data/spec/market_spec.rb +54 -0
- data/spec/order_spec.rb +108 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/support/request_stubs.rb +29 -0
- data/spec/trade_spec.rb +41 -0
- data/spec/wallet_spec.rb +33 -0
- metadata +243 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
itbit (0.0.1)
|
5
|
+
activesupport
|
6
|
+
rest_client
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.1.2)
|
12
|
+
i18n (~> 0.6, >= 0.6.9)
|
13
|
+
json (~> 1.7, >= 1.7.7)
|
14
|
+
minitest (~> 5.1)
|
15
|
+
thread_safe (~> 0.1)
|
16
|
+
tzinfo (~> 1.1)
|
17
|
+
addressable (2.3.6)
|
18
|
+
columnize (0.8.9)
|
19
|
+
crack (0.4.2)
|
20
|
+
safe_yaml (~> 1.0.0)
|
21
|
+
debugger (1.6.8)
|
22
|
+
columnize (>= 0.3.1)
|
23
|
+
debugger-linecache (~> 1.2.0)
|
24
|
+
debugger-ruby_core_source (~> 1.3.5)
|
25
|
+
debugger-linecache (1.2.0)
|
26
|
+
debugger-ruby_core_source (1.3.5)
|
27
|
+
diff-lcs (1.2.5)
|
28
|
+
i18n (0.6.9)
|
29
|
+
json (1.8.1)
|
30
|
+
minitest (5.3.5)
|
31
|
+
netrc (0.7.9)
|
32
|
+
rake (10.3.2)
|
33
|
+
rest_client (1.8.1)
|
34
|
+
netrc (~> 0.7.7)
|
35
|
+
rspec (3.0.0)
|
36
|
+
rspec-core (~> 3.0.0)
|
37
|
+
rspec-expectations (~> 3.0.0)
|
38
|
+
rspec-mocks (~> 3.0.0)
|
39
|
+
rspec-core (3.0.2)
|
40
|
+
rspec-support (~> 3.0.0)
|
41
|
+
rspec-expectations (3.0.2)
|
42
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
43
|
+
rspec-support (~> 3.0.0)
|
44
|
+
rspec-mocks (3.0.2)
|
45
|
+
rspec-support (~> 3.0.0)
|
46
|
+
rspec-support (3.0.2)
|
47
|
+
safe_yaml (1.0.3)
|
48
|
+
shoulda-matchers (2.6.1)
|
49
|
+
activesupport (>= 3.0.0)
|
50
|
+
thread_safe (0.3.4)
|
51
|
+
timecop (0.7.1)
|
52
|
+
tzinfo (1.2.1)
|
53
|
+
thread_safe (~> 0.1)
|
54
|
+
webmock (1.18.0)
|
55
|
+
addressable (>= 2.3.6)
|
56
|
+
crack (>= 0.3.2)
|
57
|
+
|
58
|
+
PLATFORMS
|
59
|
+
ruby
|
60
|
+
|
61
|
+
DEPENDENCIES
|
62
|
+
bundler (~> 1.3)
|
63
|
+
debugger
|
64
|
+
itbit!
|
65
|
+
rake
|
66
|
+
rspec
|
67
|
+
rspec-mocks
|
68
|
+
shoulda-matchers
|
69
|
+
timecop
|
70
|
+
webmock
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Nubis
|
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,146 @@
|
|
1
|
+
# ItBit
|
2
|
+
|
3
|
+
[ItBit](https://www.itbit.com) API Client library.
|
4
|
+
|
5
|
+
Does some minimal type de-serializations from stringified numbers into
|
6
|
+
BigDecimals, camelcased strings into underscored symbols, and times into integer
|
7
|
+
timestamps.
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'itbit'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install itbit
|
23
|
+
|
24
|
+
|
25
|
+
## Use Public Market Data
|
26
|
+
|
27
|
+
Itbit::XBTUSDMarketData, Itbit::XBTSGDMarketData, Itbit::XBTEURMarketData classes have methods for
|
28
|
+
fetching all public market data available.
|
29
|
+
|
30
|
+
### Ticker
|
31
|
+
|
32
|
+
ruby > Itbit::XBTUSDMarketData.ticker
|
33
|
+
=> {ask: 641.29,
|
34
|
+
ask_amt: 0.5,
|
35
|
+
bid: 622,
|
36
|
+
bid_amt: 0.0006,
|
37
|
+
high24h: 618.00000000,
|
38
|
+
high_today: 618.00000000,
|
39
|
+
last_amt: 0.00040000,
|
40
|
+
last_price: 618.00000000,
|
41
|
+
low24h: 618.00000000,
|
42
|
+
low_today: 618.00000000,
|
43
|
+
open_today: 618.00000000,
|
44
|
+
pair: symbol.to_s.upcase,
|
45
|
+
servertime_utc: 2014-06-24 20:42:35,
|
46
|
+
volume24h: 0.00040000,
|
47
|
+
volume_today: 0.00040000,
|
48
|
+
vwap24h: 618.00000000,
|
49
|
+
vwap_today: 618.00000000
|
50
|
+
}
|
51
|
+
|
52
|
+
### Order Book
|
53
|
+
|
54
|
+
ruby > Itbit::XBTUSDMarketData.orders
|
55
|
+
=> {bids: [[632.0, 38.910443037975], [630.87, 1.8], ...],
|
56
|
+
asks: [[634.9, 0.95], [648.0, 0.4809267], ...]}
|
57
|
+
|
58
|
+
### Trades
|
59
|
+
|
60
|
+
ruby > Itbit::XBTUSDMarketData.transactions
|
61
|
+
=> [ {date: unix_timestamp, price: 123.5, amount: 1.97, tid: 98375},
|
62
|
+
{date: unix_timestamp, price: 123.5, amount: 1.97, tid: 98376},
|
63
|
+
...]
|
64
|
+
|
65
|
+
## Use for Private Trading
|
66
|
+
|
67
|
+
### Authentication and default wallet
|
68
|
+
You should request your credentials to api@itbit.com, once you've got them
|
69
|
+
you can configure the gem like this:
|
70
|
+
|
71
|
+
Itbit.client_key = 'your_client_key'
|
72
|
+
Itbit.secret = 'your_secret_key'
|
73
|
+
Itbit.user_id = 'your_user_id'
|
74
|
+
|
75
|
+
You can point the gem to itbit's sandbox like this:
|
76
|
+
|
77
|
+
Itbit.sandbox = true
|
78
|
+
|
79
|
+
Itbit gives you more than one 'wallet', which is a set of btc, usd, eur and sgd
|
80
|
+
balances to use. Most of the time you'll probably end up using just one wallet,
|
81
|
+
which can be configured like this:
|
82
|
+
|
83
|
+
Itbit.default_wallet_id = 'the-wallet-id-you-want-to-use'
|
84
|
+
|
85
|
+
All api calls that need a wallet accept one as an optional argument as well.
|
86
|
+
|
87
|
+
### Get your wallets
|
88
|
+
|
89
|
+
ruby > Itbit::Wallet.all
|
90
|
+
=> { id: "3F2504E0-4F89-41D3-9A0C-0305E82C3301",
|
91
|
+
name: "Wallet",
|
92
|
+
balances: [
|
93
|
+
{ balance: "10203.25",
|
94
|
+
currency_code: :usd,
|
95
|
+
trading_balance: "10003.25"
|
96
|
+
},
|
97
|
+
{ balance: "402.110",
|
98
|
+
currency_code: :xbt,
|
99
|
+
trading_balance: "402.110"
|
100
|
+
},
|
101
|
+
{ balance: "0.00",
|
102
|
+
currency_code: :eur,
|
103
|
+
trading_balance: "0.00"
|
104
|
+
}
|
105
|
+
]
|
106
|
+
}
|
107
|
+
|
108
|
+
### Create a new wallet
|
109
|
+
|
110
|
+
ruby > Itbit::Wallet.create!('wallet_name')
|
111
|
+
|
112
|
+
### Place Bids and Asks
|
113
|
+
You can also pass in a has of keyword arguments at the end, for
|
114
|
+
sending metadata and your own order identifiers (which must be unique)
|
115
|
+
Check the spec/order_spec.rb for more examples.
|
116
|
+
|
117
|
+
ruby > Itbit::Order.create!(:buy, :xbtusd, 1.5, 500.0)
|
118
|
+
ruby > Itbit::Order.create!(:sell, :xbtsgd, 1.5, 500.0)
|
119
|
+
|
120
|
+
### List your pending or recently active orders
|
121
|
+
Orders can also be filtered and paginated by passing in keyword arguments,
|
122
|
+
Check spec/order_spec.rb for more examples.
|
123
|
+
|
124
|
+
ruby > Itbit::Order.all
|
125
|
+
|
126
|
+
### Find an order
|
127
|
+
|
128
|
+
ruby > Itbit::Order.find('some-order-id')
|
129
|
+
|
130
|
+
### Cancel an order
|
131
|
+
|
132
|
+
ruby > Itbit::Order.find('some-order-id').cancel!
|
133
|
+
|
134
|
+
### List your trades
|
135
|
+
Trades can also be filtered by date and paginated by passing in keyword arguments,
|
136
|
+
Check spec/trade_spec.rb for more examples.
|
137
|
+
|
138
|
+
ruby > Itbit::Trade.all
|
139
|
+
|
140
|
+
## Contributing
|
141
|
+
|
142
|
+
1. Fork it
|
143
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
144
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
145
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
146
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/itbit.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'itbit/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "itbit"
|
8
|
+
spec.version = Itbit::VERSION
|
9
|
+
spec.authors = ["Nubis", "Eromirou"]
|
10
|
+
spec.email = ["nb@bitex.la", "tr@bitex.la"]
|
11
|
+
spec.description = %q{API client library for itbit.com. Fetch public market
|
12
|
+
data and build trading robots}
|
13
|
+
spec.summary = "API client library for itbit.com"
|
14
|
+
spec.homepage = "http://github.com/bitex-la/itbit"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "activesupport"
|
23
|
+
spec.add_dependency "rest_client"
|
24
|
+
|
25
|
+
spec.required_ruby_version = '>= 1.9.3'
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
spec.add_development_dependency "rspec-mocks"
|
30
|
+
spec.add_development_dependency "timecop"
|
31
|
+
spec.add_development_dependency "webmock"
|
32
|
+
spec.add_development_dependency "shoulda-matchers"
|
33
|
+
end
|
data/lib/itbit.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'json'
|
3
|
+
require 'rest_client'
|
4
|
+
require 'bigdecimal'
|
5
|
+
require 'active_support'
|
6
|
+
require 'digest/sha2'
|
7
|
+
require 'base64'
|
8
|
+
Dir[File.expand_path("../itbit/*.rb", __FILE__)].each {|f| require f}
|
9
|
+
|
10
|
+
module Itbit
|
11
|
+
mattr_accessor :client_key
|
12
|
+
mattr_accessor :secret
|
13
|
+
mattr_accessor :user_id
|
14
|
+
mattr_accessor :default_wallet_id
|
15
|
+
mattr_accessor :sandbox
|
16
|
+
end
|
data/lib/itbit/api.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
module Itbit
|
4
|
+
class ApiError < StandardError; end
|
5
|
+
class Api
|
6
|
+
def self.request(verb, path, options = { })
|
7
|
+
timestamp = (Time.now.to_f * 1000).round.to_s
|
8
|
+
nonce = timestamp
|
9
|
+
prefix = Itbit.sandbox ? 'beta-api' : 'api'
|
10
|
+
payload = options.empty? || verb == :get ? nil : JSON.dump(options)
|
11
|
+
query = options.any? && verb == :get ? "?#{options.to_query}" : ''
|
12
|
+
url = "https://#{prefix}.itbit.com/v1#{path}#{query}"
|
13
|
+
|
14
|
+
signature = sign_message(verb, url, payload, nonce, timestamp)
|
15
|
+
headers = {
|
16
|
+
authorization: "#{Itbit.client_key}:#{signature}",
|
17
|
+
x_auth_timestamp: timestamp,
|
18
|
+
x_auth_nonce: nonce,
|
19
|
+
content_type: 'application/json'
|
20
|
+
}
|
21
|
+
response = RestClient::Request
|
22
|
+
.execute(:method => verb, :url => url, :payload => payload, :headers => headers)
|
23
|
+
JSON.parse(response.to_str) if response.to_str.presence
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.sign_message(verb, url, json_body, nonce, timestamp)
|
27
|
+
message = JSON.dump([verb.upcase, url, json_body || '', nonce, timestamp])
|
28
|
+
hash_digest = OpenSSL::Digest::SHA256.digest(nonce + message)
|
29
|
+
hmac_digest = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA512.new, Itbit.secret, url + hash_digest)
|
30
|
+
Base64.strict_encode64(hmac_digest)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/itbit/market.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module Itbit
|
2
|
+
# Public market data for a pair, do not use directly, use
|
3
|
+
# {XBTUSDMarketData}, {XBTSGDMarketData} and {XBTEURMarketData} instead.
|
4
|
+
# @see http://api-portal.anypoint.mulesoft.com/itbit/api/itbit-exchange#raml-console
|
5
|
+
class MarketData
|
6
|
+
|
7
|
+
# The symbol ticker conveniently formatted as a ruby Hash with
|
8
|
+
# symbolized keys.
|
9
|
+
def self.ticker
|
10
|
+
raw_ticker = Api.request(:get, "/markets/#{self.symbol}/ticker")
|
11
|
+
raw_ticker.reduce({}) do |ticker, pair|
|
12
|
+
key = pair.first.underscore.to_sym
|
13
|
+
value = case key
|
14
|
+
when :pair then pair[1].underscore.to_sym
|
15
|
+
when :servertime_utc then Time.parse(pair[1]).to_i
|
16
|
+
else pair[1].to_d
|
17
|
+
end
|
18
|
+
ticker[key] = value
|
19
|
+
ticker
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# The symbol order book as a Hash with two keys: bids and asks.
|
24
|
+
# Each of them is a list of list consisting of [price, quantity]
|
25
|
+
def self.orders
|
26
|
+
order_book = old_request("/markets/#{self.symbol.upcase}/orders").symbolize_keys
|
27
|
+
order_book.each do |key, value|
|
28
|
+
order_book[key] = value.collect { |tuple| tuple.collect(&:to_d) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# The symbol trades since tid (transaction id) as a list of hashes
|
33
|
+
# that look like {date: unix_timestamp, price: 123.5, amount: 1.97, tid: 98375}
|
34
|
+
def self.trades(tid = 0)
|
35
|
+
trades = old_request("/markets/#{symbol.upcase}/trades", since: tid)
|
36
|
+
trades.collect do |t|
|
37
|
+
t.merge(price: t['price'].to_d, amount: t['amount'].to_d)
|
38
|
+
.with_indifferent_access
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# @visibility private
|
43
|
+
def self.old_request(path, options = { })
|
44
|
+
url = "https://www.itbit.com/api/v2#{path}"
|
45
|
+
url << "?#{options.to_query}" if options.any?
|
46
|
+
JSON.parse(RestClient.get(url))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# A {MarketData} for the Bitcoin-USD pair.
|
51
|
+
class XBTUSDMarketData < MarketData
|
52
|
+
def self.symbol
|
53
|
+
'xbtusd'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# A {MarketData} for the Bitcoin-SGD pair.
|
58
|
+
class XBTSGDMarketData < MarketData
|
59
|
+
def self.symbol
|
60
|
+
'xbtsgd'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# A {MarketData} for the Bitcoin-EUR pair.
|
65
|
+
class XBTEURMarketData < MarketData
|
66
|
+
def self.symbol
|
67
|
+
'xbteur'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/itbit/order.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module Itbit
|
2
|
+
# A limit buy or sell order, translated from itbit api to a more
|
3
|
+
# suitable ruby style. Field names are underscored. Values
|
4
|
+
# for side, instrument, currency, type and status are
|
5
|
+
# underscored and symbolized.
|
6
|
+
class Order
|
7
|
+
attr_accessor :id, :wallet_id, :side, :instrument,
|
8
|
+
:currency, :type, :amount, :price, :amount_filled,
|
9
|
+
:volume_weighted_average_price, :created_time, :status,
|
10
|
+
:display_amount, :metadata, :client_order_identifier
|
11
|
+
|
12
|
+
def initialize(attrs)
|
13
|
+
attrs.each{|k, v| send("#{k.underscore}=", v)}
|
14
|
+
end
|
15
|
+
|
16
|
+
%w(side instrument currency type status).each do |attr|
|
17
|
+
define_method("#{attr}=") do |value|
|
18
|
+
instance_variable_set("@#{attr}", value.to_s.underscore.to_sym)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
%w(amount price amount_filled volume_weighted_average_price
|
23
|
+
display_amount).each do |attr|
|
24
|
+
define_method("#{attr}=") do |value|
|
25
|
+
instance_variable_set("@#{attr}", value.to_d)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def created_time=(value)
|
30
|
+
@created_time = value.is_a?(String) ? Time.parse(value).to_i : value
|
31
|
+
end
|
32
|
+
|
33
|
+
# Lists all orders for a wallet.
|
34
|
+
# Results can be filtered and paginated by passing in these options.
|
35
|
+
# wallet_id: String, defaults to Itbit.default_wallet_id
|
36
|
+
# instrument: Symbol, either :xbtusd, :xbteur, :xbtsgd
|
37
|
+
# page: Integer, starting page for pagination.
|
38
|
+
# per_page: Integer, how many to show per page.
|
39
|
+
# status: Symbol, either :submitted, :open, :filled, :cancelled, :rejected
|
40
|
+
# @return [Array<Itbit::Order>]
|
41
|
+
def self.all(opts = {})
|
42
|
+
wallet_id = opts[:wallet_id] || Itbit.default_wallet_id
|
43
|
+
params = {}
|
44
|
+
params[:instrument] = opts[:instrument].upcase if opts[:instrument]
|
45
|
+
params[:page] = opts[:page].to_i if opts[:page]
|
46
|
+
params[:perPage] = opts[:per_page].to_i if opts[:per_page]
|
47
|
+
params[:status] = opts[:status] if opts[:status]
|
48
|
+
Api.request(:get, "/wallets/#{wallet_id}/orders", params)
|
49
|
+
.collect{|o| Order.new(o) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Finds an order by id in a given wallet.
|
53
|
+
# @param id [String] The order's uuid
|
54
|
+
# @param wallet_id [String] Optional wallet's uuid, defaults to
|
55
|
+
# Itbit.default_wallet_id
|
56
|
+
def self.find(id, wallet_id = Itbit.default_wallet_id)
|
57
|
+
Order.new Api.request(:get, "/wallets/#{wallet_id}/orders/#{id}")
|
58
|
+
end
|
59
|
+
|
60
|
+
# Creates a new order
|
61
|
+
# @param side [Symbol] :buy or :sell
|
62
|
+
# @param instrument [Symbol] :xbtusd, :xbteur or :xbtsgd
|
63
|
+
# @param amount [BigDecimal] Quantity to buy or sell
|
64
|
+
# @param price [BigDecimal] Maximum price to pay when buying
|
65
|
+
# or minimum price to charge when selling.
|
66
|
+
# @param options [Hash] Optional arguments
|
67
|
+
# wallet_id: [String] The wallet to use for placing this order
|
68
|
+
# defaults to Itbit.default_wallet_id
|
69
|
+
# wait: [Boolean] Block the process and wait until this
|
70
|
+
# order moves out of the :submitted state, defaults to false.
|
71
|
+
# type: Order type, only :limit is supported at the moment
|
72
|
+
# currency: Always :xbt, but left for compatibility.
|
73
|
+
# metadata: Arbitrary JSON data for your use.
|
74
|
+
# client_order_identifier: If you have your own ID for this
|
75
|
+
# order you can pass it in this field, *Make sure it's unique!*
|
76
|
+
def self.create!(side, instrument, amount, price, options = {})
|
77
|
+
wallet_id = options[:wallet_id] || Itbit.default_wallet_id
|
78
|
+
params = {
|
79
|
+
side: side,
|
80
|
+
instrument: instrument.to_s.upcase,
|
81
|
+
amount: amount.to_d.to_s('F'),
|
82
|
+
price: price.to_d.to_s('F'),
|
83
|
+
type: options[:type] || :limit,
|
84
|
+
currency: options[:currency].try(:upcase) || 'XBT'
|
85
|
+
}
|
86
|
+
%w(metadata client_order_identifier).each do |a|
|
87
|
+
params[a.camelize(:lower)] = options[a.to_sym] if options[a.to_sym]
|
88
|
+
end
|
89
|
+
|
90
|
+
order = Order.new Api.request(:post, "/wallets/#{wallet_id}/orders", params)
|
91
|
+
retries = 0
|
92
|
+
while options[:wait] && order.status == :submitted
|
93
|
+
sleep 0.2
|
94
|
+
order = find(order.id)
|
95
|
+
retries += 1
|
96
|
+
if retries > 5000 # Wait 15 minutes for the order to be accepted.
|
97
|
+
raise StandardError.new(
|
98
|
+
"Timed out waiting for #{base_path} ##{order.id}")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
return order
|
102
|
+
end
|
103
|
+
|
104
|
+
# Cancel this order
|
105
|
+
def cancel!
|
106
|
+
Api.request(:delete, "/wallets/#{wallet_id}/orders/#{id}")
|
107
|
+
self.status = :cancelling
|
108
|
+
return self
|
109
|
+
rescue RestClient::UnprocessableEntity
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/itbit/trade.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Itbit
|
2
|
+
# List the full trading history for a given wallet
|
3
|
+
class Trade
|
4
|
+
attr_accessor :order_id, :direction, :commission_paid, :timestamp,
|
5
|
+
:rebates_applied, :currency2, :instrument, :rate, :currency1,
|
6
|
+
:rebate_currency, :currency1_amount, :currency2_amount,
|
7
|
+
:commission_currency
|
8
|
+
|
9
|
+
def initialize(attrs)
|
10
|
+
attrs.each{|k, v| send("#{k.underscore}=", v)}
|
11
|
+
end
|
12
|
+
|
13
|
+
%w(direction instrument currency1 currency2
|
14
|
+
rebate_currency commission_currency).each do |attr|
|
15
|
+
define_method("#{attr}=") do |value|
|
16
|
+
instance_variable_set("@#{attr}", value.to_s.underscore.to_sym)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
%w(commission_paid rebates_applied rate
|
21
|
+
currency1_amount currency2_amount).each do |attr|
|
22
|
+
define_method("#{attr}=") do |value|
|
23
|
+
instance_variable_set("@#{attr}", value.to_d)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def timestamp=(value)
|
28
|
+
@timestamp = value.is_a?(String) ? Time.parse(value).to_i : value
|
29
|
+
end
|
30
|
+
|
31
|
+
# Lists all trades for a wallet
|
32
|
+
# Results can be filtered and paginated by passing in these options.
|
33
|
+
# wallet_id: String, defaults to Itbit.default_wallet_id
|
34
|
+
# page: Integer, starting page for pagination.
|
35
|
+
# per_page: Integer, how many to show per page.
|
36
|
+
# range_start: Integer timestamp, start showing at this date.
|
37
|
+
# range_end: Integer timestamp, stop showing at this date.
|
38
|
+
# @return [Array<Itbit::Trade>]
|
39
|
+
def self.all(opts = {})
|
40
|
+
wallet_id = opts[:wallet_id] || Itbit.default_wallet_id
|
41
|
+
params = {}
|
42
|
+
%w(range_start range_end page per_page).each do |a|
|
43
|
+
params[a.camelize(:lower)] = opts[a.to_sym].to_i if opts[a.to_sym]
|
44
|
+
end
|
45
|
+
response = Api.request(:get, "/wallets/#{wallet_id}/trades", params)
|
46
|
+
{
|
47
|
+
total_number_of_records: response['totalNumberOfRecords'].to_i,
|
48
|
+
current_page_number: response['currentPageNumber'].to_i,
|
49
|
+
latest_execution_id: response['latestExecutionId'].to_i,
|
50
|
+
records_per_page: response['recordsPerPage'].to_i,
|
51
|
+
trading_history: response['tradingHistory'].collect{|x| new(x) }
|
52
|
+
}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/itbit/wallet.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Itbit
|
2
|
+
# Your ItBit wallets, translated from itbit api to a more
|
3
|
+
# suitable ruby style. Field names are underscored.
|
4
|
+
# Values for balance and trading_balance are
|
5
|
+
# converted to BigDecimal
|
6
|
+
class Wallet
|
7
|
+
# Lists all wallets.
|
8
|
+
def self.all
|
9
|
+
Api.request(:get, "/wallets", userId: Itbit.user_id)
|
10
|
+
.collect { |wallet| translate_wallet(wallet) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Creates a new order
|
14
|
+
# @param name [String] The name for the new wallet.
|
15
|
+
def self.create!(name)
|
16
|
+
translate_wallet(Api.request(:post, "/wallets", name: name, userId: Itbit.user_id))
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.translate_wallet(wallet)
|
20
|
+
wallet.symbolize_keys!
|
21
|
+
wallet[:balances] = wallet[:balances].dup.collect do |b|
|
22
|
+
{ balance: b['balance'].to_d,
|
23
|
+
currency_code: b['currencyCode'].downcase.to_sym,
|
24
|
+
trading_balance: b['tradingBalance'].to_d
|
25
|
+
}
|
26
|
+
end
|
27
|
+
wallet
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"pair":"XBTEUR","bid":"622","bidAmt":"0.0006","ask":"641.29","askAmt":"0.5","lastPrice":"618.00000000","lastAmt":"0.00040000","volume24h":"0.00040000","volumeToday":"0.00040000","high24h":"618.00000000","low24h":"618.00000000","highToday":"618.00000000","lowToday":"618.00000000","openToday":"618.00000000","vwapToday":"618.00000000","vwap24h":"618.00000000","servertimeUTC":"2014-06-24T20:42:35.6160000Z"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"pair":"XBTSGD","bid":"622","bidAmt":"0.0006","ask":"641.29","askAmt":"0.5","lastPrice":"618.00000000","lastAmt":"0.00040000","volume24h":"0.00040000","volumeToday":"0.00040000","high24h":"618.00000000","low24h":"618.00000000","highToday":"618.00000000","lowToday":"618.00000000","openToday":"618.00000000","vwapToday":"618.00000000","vwap24h":"618.00000000","servertimeUTC":"2014-06-24T20:42:35.6160000Z"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"pair":"XBTUSD","bid":"622","bidAmt":"0.0006","ask":"641.29","askAmt":"0.5","lastPrice":"618.00000000","lastAmt":"0.00040000","volume24h":"0.00040000","volumeToday":"0.00040000","high24h":"618.00000000","low24h":"618.00000000","highToday":"618.00000000","lowToday":"618.00000000","openToday":"618.00000000","vwapToday":"618.00000000","vwap24h":"618.00000000","servertimeUTC":"2014-06-24T20:42:35.6160000Z"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"id":"8fd820d3-baff-4d6f-9439-ff03d816c7ce","walletId":"wallet-000","side":"buy","instrument":"XBTUSD","type":"limit","amount":"1.00500000","displayAmount":"1.00500000","price":"100.00000000","volumeWeightedAveragePrice":"0","amountFilled":"0","createdTime":"2014-11-06T16:09:47.6800000Z","status":"open","metadata":{"foo":"bar"},"clientOrderIdentifier":"o"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"id":"8fd820d3-baff-4d6f-9439-ff03d816c7ce","walletId":"b440efce-a83c-4873-8833-802a1022b476","side":"buy","instrument":"XBTUSD","type":"limit","amount":"1.00500000","displayAmount":"1.00500000","price":"100.00000000","volumeWeightedAveragePrice":"0","amountFilled":"0","createdTime":"2014-11-06T16:09:47.6800000Z","status":"submitted","metadata":{"foo":"bar"},"clientOrderIdentifier":"o"}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"bids":[["639.21","1.95"],["637.0","0.47"],["630.0","1.58"]],"asks":[["642.4","0.4"],["643.3","0.95"],["644.3","0.25"]]}
|
@@ -0,0 +1 @@
|
|
1
|
+
[{"id":"8fd820d3-baff-4d6f-9439-ff03d816c7ce","walletId":"b440efce-a83c-4873-8833-802a1022b476","side":"buy","instrument":"XBTUSD","type":"limit","amount":"1.00500000","displayAmount":"1.00500000","price":"100.00000000","volumeWeightedAveragePrice":"0","amountFilled":"0","createdTime":"2014-11-06T16:09:47.6800000Z","status":"open","metadata":{"foo":"bar"},"clientOrderIdentifier":"o"},{"id":"8fd820d3-baff-4d6f-9439-ff03d816c7FF","walletId":"b440efce-a83c-4873-8833-802a1022b476","side":"sell","instrument":"XBTUSD","type":"limit","amount":"1.00500000","displayAmount":"1.00500000","price":"100.00000000","volumeWeightedAveragePrice":"0","amountFilled":"0","createdTime":"2014-11-06T16:09:47.6800000Z","status":"submitted","metadata":{"foo":"bar"},"clientOrderIdentifier":"o"}]
|
@@ -0,0 +1 @@
|
|
1
|
+
[{"date":1415218064,"price":"340.00000000","amount":"1.97180000","tid":98375},{"date":1415218064,"price":"340.00000000","amount":"1.44730000","tid":98374},{"date":1415217474,"price":"339.75000000","amount":"1.93820000","tid":98373},{"date":1415217473,"price":"339.75000000","amount":"1.62210000","tid":98372},{"date":1415217455,"price":"339.75000000","amount":"1.31850000","tid":98371},{"date":1415217454,"price":"339.75000000","amount":"1.96260000","tid":98370}]
|
@@ -0,0 +1 @@
|
|
1
|
+
{"totalNumberOfRecords":"6","currentPageNumber":"1","latestExecutionId":"332","recordsPerPage":"50","tradingHistory":[{"orderId":"c78363f9-1169-4dbb-b256-0756880dc625","direction":"Buy","commissionPaid":"0.40250000","timestamp":"2014-03-04T16:02:28.0070000Z","rebatesApplied":"0","currency2":"USD","instrument":"XBTUSD","rate":"575.00000000","currency1":"XBT","rebateCurrency":"USD","currency1Amount":"0.10000000","currency2Amount":"57.5000000000000000","commissionCurrency":"USD"},{"orderId":"685868a7-911e-4271-b580-6b307b3fc1af","direction":"Buy","commissionPaid":"0.40250000","timestamp":"2014-03-04T16:02:07.3530000Z","rebatesApplied":"0","currency2":"USD","instrument":"XBTUSD","rate":"575.00000000","currency1":"XBT","rebateCurrency":"USD","currency1Amount":"0.10000000","currency2Amount":"57.5000000000000000","commissionCurrency":"USD"}]}
|
@@ -0,0 +1 @@
|
|
1
|
+
{"id":"3F2504E0-4F89-41D3-9A0C-0305E82C3301","name":"Wallet","balances":[{"balance":"10203.25","currencyCode":"USD","tradingBalance":"10003.25"},{"balance":"402.110","currencyCode":"XBT","tradingBalance":"402.110"},{"balance":"0.00","currencyCode":"EUR","tradingBalance":"0.00"}]}
|
@@ -0,0 +1 @@
|
|
1
|
+
[{"id":"3F2504E0-4F89-41D3-9A0C-0305E82C3301","name":"Wallet","balances":[{"balance":"10203.25","currencyCode":"USD","tradingBalance":"10003.25"},{"balance":"402.110","currencyCode":"XBT","tradingBalance":"402.110"},{"balance":"0.00","currencyCode":"EUR","tradingBalance":"0.00"}]},{"id":"093DA2D8-12D8-408E-B59D-D4730E5F189D","name":"Trading","balances":[{"balance":"0","currencyCode":"USD","tradingBalance":"0"},{"balance":"0.00","currencyCode":"XBT","tradingBalance":"0.00"},{"balance":"0.00","currencyCode":"EUR","tradingBalance":"0.00"}]}]
|
data/spec/market_spec.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itbit::MarketData do
|
4
|
+
|
5
|
+
{ xbtusd: Itbit::XBTUSDMarketData,
|
6
|
+
xbtsgd: Itbit::XBTSGDMarketData,
|
7
|
+
xbteur: Itbit::XBTEURMarketData
|
8
|
+
}.each do |symbol, market_data|
|
9
|
+
describe "when getting #{symbol} market data" do
|
10
|
+
it "gets the ticker" do
|
11
|
+
stub_get("/markets/#{symbol}/ticker", "market_ticker_#{symbol}")
|
12
|
+
market_data.ticker.should == {
|
13
|
+
ask: "641.29".to_d,
|
14
|
+
ask_amt: "0.5".to_d,
|
15
|
+
bid: "622".to_d,
|
16
|
+
bid_amt: "0.0006".to_d,
|
17
|
+
high24h: "618.00000000".to_d,
|
18
|
+
high_today: "618.00000000".to_d,
|
19
|
+
last_amt: "0.00040000".to_d,
|
20
|
+
last_price: "618.00000000".to_d,
|
21
|
+
low24h: "618.00000000".to_d,
|
22
|
+
low_today: "618.00000000".to_d,
|
23
|
+
open_today: "618.00000000".to_d,
|
24
|
+
pair: symbol,
|
25
|
+
servertime_utc: Time.parse("2014-06-24T20:42:35.6160000Z").to_i,
|
26
|
+
volume24h: "0.00040000".to_d,
|
27
|
+
volume_today: "0.00040000".to_d,
|
28
|
+
vwap24h: "618.00000000".to_d,
|
29
|
+
vwap_today: "618.00000000".to_d,
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "gets the order book" do
|
34
|
+
stub_old("/markets/#{symbol.upcase}/orders", 'order_book')
|
35
|
+
market_data.orders.should == {
|
36
|
+
bids: [[639.21,1.95],[637.0,0.47],[630.0,1.58]],
|
37
|
+
asks: [[642.4,0.4],[643.3,0.95],[644.3,0.25]]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "gets the trades" do
|
42
|
+
stub_old("/markets/#{symbol.upcase}/trades", 'trades', since: 0)
|
43
|
+
trades = market_data.trades
|
44
|
+
trades.size.should == 6
|
45
|
+
trades.first.tap do |t|
|
46
|
+
t[:date].should == 1415218064
|
47
|
+
t[:price].should == 340.0
|
48
|
+
t[:amount].should == 1.9718
|
49
|
+
t[:tid].should == 98375
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/spec/order_spec.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itbit::Order do
|
4
|
+
it 'lists all' do
|
5
|
+
stub_get("/wallets/wallet-000/orders", 'orders')
|
6
|
+
orders = Itbit::Order.all
|
7
|
+
orders.size.should == 2
|
8
|
+
orders.each{|o| o.should be_an Itbit::Order}
|
9
|
+
orders.first.side.should == :buy
|
10
|
+
orders.last.side.should == :sell
|
11
|
+
orders.first.tap do |order|
|
12
|
+
order.id.should == "8fd820d3-baff-4d6f-9439-ff03d816c7ce"
|
13
|
+
order.wallet_id.should == "b440efce-a83c-4873-8833-802a1022b476"
|
14
|
+
order.side.should == :buy
|
15
|
+
order.instrument.should == :xbtusd
|
16
|
+
order.type.should == :limit
|
17
|
+
order.amount.should == "1.005".to_d
|
18
|
+
order.price.should == "100".to_d
|
19
|
+
order.amount_filled.should == "0".to_d
|
20
|
+
order.volume_weighted_average_price.should == '0'.to_d
|
21
|
+
order.created_time.should == 1415290187
|
22
|
+
order.status.should == :open
|
23
|
+
order.metadata.should == {"foo" => "bar"}
|
24
|
+
order.client_order_identifier.should == "o"
|
25
|
+
order.display_amount = "1.0050000".to_d
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'Accepts filtering by instrument, status and pagination' do
|
30
|
+
stub_get("/wallets/wallet-001/orders", 'orders',
|
31
|
+
instrument: 'XBTUSD',
|
32
|
+
status: 'open',
|
33
|
+
page: '1',
|
34
|
+
perPage: '200')
|
35
|
+
orders = Itbit::Order.all(
|
36
|
+
wallet_id: 'wallet-001',
|
37
|
+
instrument: :xbtusd,
|
38
|
+
status: :open,
|
39
|
+
page: 1,
|
40
|
+
per_page: 200)
|
41
|
+
orders.size.should == 2
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'Finds an order' do
|
45
|
+
stub_get("/wallets/wallet-000/orders/8fd820d3-baff-4d6f-9439-ff03d816c7ce", 'order')
|
46
|
+
order = Itbit::Order.find("8fd820d3-baff-4d6f-9439-ff03d816c7ce")
|
47
|
+
order.should be_an Itbit::Order
|
48
|
+
order.id.should == "8fd820d3-baff-4d6f-9439-ff03d816c7ce"
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'Places an order' do
|
52
|
+
stub_post("/wallets/wallet-000/orders", 'order',
|
53
|
+
side: 'buy',
|
54
|
+
instrument: 'XBTUSD',
|
55
|
+
amount: '1.0005',
|
56
|
+
price: '100.0',
|
57
|
+
type: 'limit',
|
58
|
+
currency: 'XBT'
|
59
|
+
)
|
60
|
+
order = Itbit::Order.create!(:buy, :xbtusd, 1.0005, 100.0)
|
61
|
+
order.should be_an Itbit::Order
|
62
|
+
order.status.should == :submitted
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'Places an order with optional arguments' do
|
66
|
+
stub_post("/wallets/wallet-000/orders", 'order', {
|
67
|
+
side: 'buy',
|
68
|
+
instrument: 'XBTUSD',
|
69
|
+
amount: '1.0005',
|
70
|
+
price: '100.0',
|
71
|
+
type: 'limit',
|
72
|
+
currency: 'XBT',
|
73
|
+
metadata: {"foo" => "bar"},
|
74
|
+
clientOrderIdentifier: 'unique_yet_optional'
|
75
|
+
})
|
76
|
+
order = Itbit::Order.create!(:buy, :xbtusd, 1.0005, 100.0,
|
77
|
+
type: :limit,
|
78
|
+
metadata: {"foo" => "bar"},
|
79
|
+
client_order_identifier: 'unique_yet_optional')
|
80
|
+
order.should be_an Itbit::Order
|
81
|
+
order.status.should == :submitted
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'places for btc and waits until processed by the matching engine' do
|
85
|
+
stub_post("/wallets/wallet-000/orders", 'order',
|
86
|
+
side: 'buy',
|
87
|
+
instrument: 'XBTUSD',
|
88
|
+
amount: '1.0005',
|
89
|
+
price: '100.0',
|
90
|
+
type: 'limit',
|
91
|
+
currency: 'XBT'
|
92
|
+
)
|
93
|
+
stub_get("/wallets/wallet-000/orders/8fd820d3-baff-4d6f-9439-ff03d816c7ce",
|
94
|
+
'open_order')
|
95
|
+
order = Itbit::Order.create!(:buy, :xbtusd, 1.0005, 100.0, wait: true)
|
96
|
+
order.should be_an Itbit::Order
|
97
|
+
order.status.should == :open
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'cancels one' do
|
101
|
+
url = "/wallets/wallet-000/orders/8fd820d3-baff-4d6f-9439-ff03d816c7ce"
|
102
|
+
stub_delete(url, nil)
|
103
|
+
stub_get(url, 'open_order')
|
104
|
+
order = Itbit::Order.find("8fd820d3-baff-4d6f-9439-ff03d816c7ce")
|
105
|
+
order.cancel!
|
106
|
+
order.status.should == :cancelling
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'itbit'
|
5
|
+
require 'webmock/rspec'
|
6
|
+
require 'shoulda/matchers'
|
7
|
+
require 'timecop'
|
8
|
+
|
9
|
+
Dir[File.expand_path("../support/**/*.rb", __FILE__)].each {|f| require f}
|
10
|
+
|
11
|
+
RSpec.configure do |config|
|
12
|
+
config.include RequestStubs
|
13
|
+
|
14
|
+
config.mock_with :rspec do |mocks|
|
15
|
+
mocks.yield_receiver_to_any_instance_implementation_blocks = true
|
16
|
+
mocks.syntax = [:expect, :should]
|
17
|
+
end
|
18
|
+
config.expect_with :rspec do |c|
|
19
|
+
c.syntax = [:expect, :should]
|
20
|
+
end
|
21
|
+
|
22
|
+
config.after(:each) do
|
23
|
+
Timecop.return
|
24
|
+
end
|
25
|
+
|
26
|
+
config.before :each do
|
27
|
+
Itbit.client_key = 'the-client-key'
|
28
|
+
Itbit.secret = 'the-secret'
|
29
|
+
Itbit.user_id = 'the-user-id'
|
30
|
+
Itbit.default_wallet_id = 'wallet-000'
|
31
|
+
end
|
32
|
+
|
33
|
+
config.order = "random"
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
module RequestStubs
|
3
|
+
%w(get post delete).each do |verb|
|
4
|
+
define_method("stub_#{verb}") do |path, fixture, options = {}|
|
5
|
+
stub_api(verb.to_sym, "https://api.itbit.com/v1#{path}", fixture, options)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def stub_old(path, fixture, options = { })
|
10
|
+
stub_api(:get, "https://www.itbit.com/api/v2#{path}", fixture, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def stub_api(method, url, fixture, options)
|
14
|
+
response_body = if fixture
|
15
|
+
File.read(File.expand_path("../../fixtures/#{fixture}.json", __FILE__))
|
16
|
+
else
|
17
|
+
''
|
18
|
+
end
|
19
|
+
with = if options.empty?
|
20
|
+
{}
|
21
|
+
elsif method == :get
|
22
|
+
{query: options}
|
23
|
+
else
|
24
|
+
{body: options.to_json}
|
25
|
+
end
|
26
|
+
stub_request(method, url)
|
27
|
+
.with(with).to_return(status: 200, body: response_body)
|
28
|
+
end
|
29
|
+
end
|
data/spec/trade_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itbit::Trade do
|
4
|
+
it 'lists all' do
|
5
|
+
stub_get("/wallets/wallet-000/trades", 'user_trades')
|
6
|
+
trades = Itbit::Trade.all
|
7
|
+
trades[:trading_history].size.should == 2
|
8
|
+
trades[:trading_history].each{|o| o.should be_an Itbit::Trade}
|
9
|
+
trades[:trading_history].first.tap do |trade|
|
10
|
+
trade.order_id.should == "c78363f9-1169-4dbb-b256-0756880dc625"
|
11
|
+
trade.direction.should == :buy
|
12
|
+
trade.commission_paid.should == "0.40250000".to_d
|
13
|
+
trade.timestamp.should == Time.parse("2014-03-04T16:02:28.0070000Z").to_i
|
14
|
+
trade.rebates_applied.should == 0
|
15
|
+
trade.currency2.should == :usd
|
16
|
+
trade.instrument.should == :xbtusd
|
17
|
+
trade.rate.should == "575.00000000".to_d
|
18
|
+
trade.currency1.should == :xbt
|
19
|
+
trade.rebate_currency.should == :usd
|
20
|
+
trade.currency1_amount.should == "0.10000000".to_d
|
21
|
+
trade.currency2_amount.should == "57.5000000000000000".to_d
|
22
|
+
trade.commission_currency.should == :usd
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'Accepts filtering by instrument, status and pagination' do
|
27
|
+
stub_get("/wallets/wallet-001/trades", 'user_trades',
|
28
|
+
rangeStart: 0,
|
29
|
+
rangeEnd: 100000,
|
30
|
+
page: '1',
|
31
|
+
perPage: '200')
|
32
|
+
trades = Itbit::Trade.all(
|
33
|
+
wallet_id: 'wallet-001',
|
34
|
+
range_start: 0,
|
35
|
+
range_end: 100000,
|
36
|
+
page: 1,
|
37
|
+
per_page: 200)
|
38
|
+
trades[:trading_history].size.should == 2
|
39
|
+
trades[:trading_history].each{|o| o.should be_an Itbit::Trade}
|
40
|
+
end
|
41
|
+
end
|
data/spec/wallet_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itbit::Wallet do
|
4
|
+
it 'lists all' do
|
5
|
+
stub_get("/wallets", 'wallets', userId: Itbit.user_id)
|
6
|
+
wallets = Itbit::Wallet.all
|
7
|
+
wallets.size.should == 2
|
8
|
+
wallets.first.should == {
|
9
|
+
id: "3F2504E0-4F89-41D3-9A0C-0305E82C3301",
|
10
|
+
name: "Wallet",
|
11
|
+
balances: [
|
12
|
+
{ balance: "10203.25".to_d,
|
13
|
+
currency_code: :usd,
|
14
|
+
trading_balance: "10003.25".to_d
|
15
|
+
},
|
16
|
+
{ balance: "402.110".to_d,
|
17
|
+
currency_code: :xbt,
|
18
|
+
trading_balance: "402.110".to_d
|
19
|
+
},
|
20
|
+
{ balance: "0.00".to_d,
|
21
|
+
currency_code: :eur,
|
22
|
+
trading_balance: "0.00".to_d
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'creates a wallet' do
|
29
|
+
stub_post("/wallets", 'wallet', name: 'Wallet', userId: Itbit.user_id)
|
30
|
+
wallet = Itbit::Wallet.create!('Wallet')
|
31
|
+
wallet[:id].should == "3F2504E0-4F89-41D3-9A0C-0305E82C3301"
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: itbit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Nubis
|
9
|
+
- Eromirou
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-11-07 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: rest_client
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :runtime
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: bundler
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ~>
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '1.3'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rake
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: rspec
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rspec-mocks
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: timecop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ! '>='
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ! '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
- !ruby/object:Gem::Dependency
|
128
|
+
name: webmock
|
129
|
+
requirement: !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ! '>='
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
type: :development
|
136
|
+
prerelease: false
|
137
|
+
version_requirements: !ruby/object:Gem::Requirement
|
138
|
+
none: false
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
- !ruby/object:Gem::Dependency
|
144
|
+
name: shoulda-matchers
|
145
|
+
requirement: !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
147
|
+
requirements:
|
148
|
+
- - ! '>='
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
type: :development
|
152
|
+
prerelease: false
|
153
|
+
version_requirements: !ruby/object:Gem::Requirement
|
154
|
+
none: false
|
155
|
+
requirements:
|
156
|
+
- - ! '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
description: ! "API client library for itbit.com. Fetch public market\n data
|
160
|
+
and build trading robots"
|
161
|
+
email:
|
162
|
+
- nb@bitex.la
|
163
|
+
- tr@bitex.la
|
164
|
+
executables: []
|
165
|
+
extensions: []
|
166
|
+
extra_rdoc_files: []
|
167
|
+
files:
|
168
|
+
- .gitignore
|
169
|
+
- .rspec
|
170
|
+
- Gemfile
|
171
|
+
- Gemfile.lock
|
172
|
+
- LICENSE.txt
|
173
|
+
- README.md
|
174
|
+
- Rakefile
|
175
|
+
- itbit.gemspec
|
176
|
+
- lib/itbit.rb
|
177
|
+
- lib/itbit/api.rb
|
178
|
+
- lib/itbit/market.rb
|
179
|
+
- lib/itbit/order.rb
|
180
|
+
- lib/itbit/trade.rb
|
181
|
+
- lib/itbit/version.rb
|
182
|
+
- lib/itbit/wallet.rb
|
183
|
+
- spec/fixtures/market_ticker_xbteur.json
|
184
|
+
- spec/fixtures/market_ticker_xbtsgd.json
|
185
|
+
- spec/fixtures/market_ticker_xbtusd.json
|
186
|
+
- spec/fixtures/open_order.json
|
187
|
+
- spec/fixtures/order.json
|
188
|
+
- spec/fixtures/order_book.json
|
189
|
+
- spec/fixtures/orders.json
|
190
|
+
- spec/fixtures/trades.json
|
191
|
+
- spec/fixtures/user_trades.json
|
192
|
+
- spec/fixtures/wallet.json
|
193
|
+
- spec/fixtures/wallets.json
|
194
|
+
- spec/market_spec.rb
|
195
|
+
- spec/order_spec.rb
|
196
|
+
- spec/spec_helper.rb
|
197
|
+
- spec/support/request_stubs.rb
|
198
|
+
- spec/trade_spec.rb
|
199
|
+
- spec/wallet_spec.rb
|
200
|
+
homepage: http://github.com/bitex-la/itbit
|
201
|
+
licenses:
|
202
|
+
- MIT
|
203
|
+
post_install_message:
|
204
|
+
rdoc_options: []
|
205
|
+
require_paths:
|
206
|
+
- lib
|
207
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
208
|
+
none: false
|
209
|
+
requirements:
|
210
|
+
- - ! '>='
|
211
|
+
- !ruby/object:Gem::Version
|
212
|
+
version: 1.9.3
|
213
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
214
|
+
none: false
|
215
|
+
requirements:
|
216
|
+
- - ! '>='
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: '0'
|
219
|
+
requirements: []
|
220
|
+
rubyforge_project:
|
221
|
+
rubygems_version: 1.8.23
|
222
|
+
signing_key:
|
223
|
+
specification_version: 3
|
224
|
+
summary: API client library for itbit.com
|
225
|
+
test_files:
|
226
|
+
- spec/fixtures/market_ticker_xbteur.json
|
227
|
+
- spec/fixtures/market_ticker_xbtsgd.json
|
228
|
+
- spec/fixtures/market_ticker_xbtusd.json
|
229
|
+
- spec/fixtures/open_order.json
|
230
|
+
- spec/fixtures/order.json
|
231
|
+
- spec/fixtures/order_book.json
|
232
|
+
- spec/fixtures/orders.json
|
233
|
+
- spec/fixtures/trades.json
|
234
|
+
- spec/fixtures/user_trades.json
|
235
|
+
- spec/fixtures/wallet.json
|
236
|
+
- spec/fixtures/wallets.json
|
237
|
+
- spec/market_spec.rb
|
238
|
+
- spec/order_spec.rb
|
239
|
+
- spec/spec_helper.rb
|
240
|
+
- spec/support/request_stubs.rb
|
241
|
+
- spec/trade_spec.rb
|
242
|
+
- spec/wallet_spec.rb
|
243
|
+
has_rdoc:
|