itbit 0.0.1
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.
- 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:
|